aws s3 全名是 Simple Storage Service,是对象存储服务(oss),对象存储服务作为网盘的优点这里就不细说了,其提供的统一接口sdk几乎涵盖了所有语言。除了通用性以外,其以上传下载为核心的接口几乎可以满足所有的业务需求。本人在几年的工作中对接过不同sdk的接口,可以说aws s3的接口,只有你想不到,没有他做不到,虽然前提是你能从他那天书一样的接口文档中找到你想要的内容。下文是记录下对接过程中s3几个比较重要的接口,对象存储是私有化部署,下文代码是以php为开发语言(哪个开发语言不重要,只要语言底层支持io多路复用性能上都不会有太大区别)。
连接
access_key和secret_key是每个对象存储都会提供的信息,在endpoint填写自己私有化部署或者其它云服务商的对象存储地址
use Aws\S3\S3Client;
use Aws\Credentials\Credentials;
use Aws\Exception\AwsException;
use Aws\Exception\MultipartUploadException;
use Aws\S3\ObjectUploader;
use Aws\S3\MultipartUploader;
$this->client = new S3Client([
'endpoint' => $endpoint,
'region' => 'us-east-1', //需要随便一个region
'service_name' => 's3',
'verify' => 'False',
'credentials' => new Credentials($aws_access_key_id, $aws_secret_access_key);,
'version' => 'latest'
]);
上传
1、实现简单的文件上传
$uploader = new ObjectUploader(
$this->client,
$bucket,
$key,
$source
);
$result = $uploader->upload();
2、分片上传
如果文件较大,上传时间会变慢,而且代码会堵塞在upload部分,
所以可以通过接口提供分片上传:
$source = '/path/to/large/file.zip';
$uploader = new MultipartUploader($s3Client, $source, [
'bucket' => 'your-bucket',
'key' => 'my-file.zip',
'before_initiate' => function (\Aws\Command $command) {
$command['CacheControl'] = 'max-age=3600';
}
]);
大致原理是对一个文件进行切割,然后并发上传,该方法有多个参数,可控制每个分片的大小,同时上传的分片数量或者每个分片上传前、上传后、上传失败等回调操作,十分灵活。
3、异步分片上传
虽然上面的分片上传操作可减少大文件上传时间,但是像一些上传文件的操作,从web端上传到服务端,需要先上传整个文件到服务端,然后服务端再上传文件到oss端。这样的话如果服务端使用oss做文件存储,就算web端使用了分片上传到服务端,服务端也得等待所有分片上传完毕,合成最终文件,再将文件上传到oss端,这样上传时间会变多。所以要优化分片上传的操作,可以使用s3提供的异步分片上传功能:
$res = $this->client->createMultipartUpload(array_merge([
'Bucket' => $bucket, // REQUIRED
'Key' => $key, // REQUIRED
'Expires' => $expire,
], $args));
return $res;
上面代码提供了一个分片上传任务,并返回一个upload_id,这样当web端上传了一个文件的分片,可根据upload_id先上传到oss端,oss端将分片暂时保存到内存中(需要根据分片顺序填写PartNumber):
$res = $this->client->uploadPart([
'Body' => $source,
'Bucket' => $bucket, // REQUIRED
'Key' => $key, // REQUIRED
'PartNumber' => $PartNumber, // REQUIRED
'UploadId' => $UploadId, // REQUIRED
]);
return $res;
当web端所有分片都上传完毕,同时所有分片也上传到oss端后,调用完成上传接口:
$res = $this->client->completeMultipartUpload([
'Bucket' => $bucket, // REQUIRED
'Key' => $key, // REQUIRED
'MultipartUpload' => $MultipartUpload,
'UploadId' => $UploadId, // REQUIRED
]);
return $res;
这样就能实现web端上传与服务端上传oss的并发进行,可节省大量的上传时间。
上面是上传的主要功能,每个接口还提供了大量的参数和回调方法,主要很多参数和回调现在也没有需求用得上,在此就不一一列举
下载
php sdk的下载功能比起python来说功能比较简陋,但也可以实现分片下载功能,涉及到下载文件的只有一个接口:
$res = $this->client->getObject(array_merge([
'Bucket' => $bucket,
'Key' => $key,
'Range' => 'bytes=0-9',
'SaveAs' => '/path/to/save/file'
], $args));
return $res;
其中比较重要的是range参数,它实现了浏览器的断点续传功能,当浏览器请求文件下载时,如果文件存储在oss,普通情况下需要把文件先下载下来,然后再让浏览器分片下载,但是通过传递range参数,把文件对应的分片从oss下载下来,然后再发送给web端,实现了持续从oss端下载到web端,中间无阻隔
分页查询
普通的分页查询可使用listObjects:
$result = $this->client->listObjects([
'Bucket' => '<string>', // REQUIRED
'Delimiter' => '<string>',
'EncodingType' => 'url',
'ExpectedBucketOwner' => '<string>',
'Marker' => '<string>',
'MaxKeys' => <integer>,
'Prefix' => '<string>',
]);
使用MaxKeys作为每页数量,Marker作为每次请求一页中的最后一个文件,作为key传给下一页请求的marker,类似offset的作用。
实际上由于oss没有分页功能,普通这样的请求只能从第一页开始查询直到想查询的页数,浪费大量的资源。所以可以通过oss提供的分页迭代器来实现:
$args['Bucket'] = $bucket;
$args['MaxKeys'] = $limit;
$results = $this->client->getPaginator('ListObjects', $args);
$total_results = [];
for ($i = 0; $i < $offset; $i++) {
if ($results->valid()) {
$results->next();
} else {
break;
}
}
if ($results->valid()) {
array_push($total_results, $results->current());
$results->next();
}
return $total_results;
迭代器是几乎所有语言都有实现的功能,他的作用是保存代码上下文状态,例如上面的代码,通过迭代器形成一个分页工具,当使用next进行翻页,底层代码不需要请求接口,而是通过指针指向下一页,这样就能实现快速翻页,获取对应页数的文件。