云盘上使用aws_s3_sdk for php

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进行翻页,底层代码不需要请求接口,而是通过指针指向下一页,这样就能实现快速翻页,获取对应页数的文件。

posted on 2021-05-07 17:30  睿江云  阅读(144)  评论(0编辑  收藏  举报