5. 文件上传¶
在NOS中用户的基本操作单元是对象,亦可以理解为文件,NOS PHP SDK提供了丰富的上传接口,可以通过以下的方式上传文件:
- 字符串上传
- 本地文件上传
- 分片上传
字符串上传、本地文件上传最大为100M,分片上传对文件大小没有限制
5.1. 字符串上传¶
您可以使用S3Client::putObject上传字符串内容到文件中,具体实现如下:
<?php
/**
* @param $s3Client S3Client实例
* @param $bucketName 桶名
* @param $objectName 对象名
* @param $Body 'mixed type: string|resource|\Guzzle\Http\EntityBodyInterface'
*/
function putObject($s3Client,$bucketName,$objectName,$Body){//Body中传入字符串即可
try{
$result = $s3Client->putObject(['Bucket'=>$bucketName,
'Key'=>$objectName,
'Body'=>$Body]);
echo $result;
} catch (\Aws\Exception\AwsException $exception){
echo $exception->getMessage();
}
}
Attention
- 上传的字符串内容不超过100M
5.2. 本地文件上传¶
您可以使用S3Client::uploadFile上传文件内容,具体实现如下:
<?php
/**
* @param $s3Client S3Client实例
* @param $bucketName 桶名
* @param $objectName 对象名
* @param $Body 'mixed type: string|resource|\Guzzle\Http\EntityBodyInterface'
*/
function putObject($s3Client,$bucketName,$objectName,$Body){//Body传入文件指针
try{
$result = $s3Client->putObject(['Bucket'=>$bucketName,
'Key'=>$objectName,
'Body'=>$Body]);
echo $result;
} catch (\Aws\Exception\AwsException $exception){
echo $exception->getMessage();
}
}
putObject($s3Client,$bucketName,$objectName,fopen(__FILE__,'r'));
Attention
- 上传的文件内容不超过100M
5.3. 分片上传¶
除了通过putObject接口上传文件到NOS之外,NOS还提供了另外一种上传模式-分片上传,用户可以在如下应用场景内(但不限于此),使用分片上传模式,如:
- 需支持断点上传
- 上传超过100M的文件
- 网络条件较差,经常和NOS服务器断开连接
- 上传文件之前无法确定文件的大小
5.3.1. 分片上传本地文件¶
您可以使用S3Client::upload来上传一个文件,该方法封装了细节,会根据传入的文件及分片的大小自动选择上传方式,对于分片大小小于文件大下的情况下会使用分片上传:
<?php
/**
* @param $s3Client S3Client实例
* @param $bucketName 桶名
* @param $objectName 对象名
* @param $body 'mixed type: string|resource|\Guzzle\Http\EntityBodyInterface'
*/
function uploadObject($s3Client,$bucketName,$objectName,$body){
try{
$result = $s3Client->upload($bucketName,$objectName,$body,'public-read');
echo $result;
} catch (\Aws\Exception\AwsException $exception){
echo $exception->getMessage();
}
}
uploadObject($s3Client,$bucketName,$uploadObjectKey,fopen($filePath,'r'));
Attention
- 默认的分片大小为5M,可以在acl后面加入option选项,加入最小的分片大小时,不能小于16K,负责NOS不接受该请求
5.3.2. 原始接口分片上传¶
您可以使用原始的分片上传接口进行分片上传,一般流程如下所示:
- 初始化一个分片上传任务(createMultipartUpload)
- 逐个或并行上传分片(UploadPart)
- 完成分片上传(CompleteMultipartUpload)或者取消分片上传(AbortMultipartUpload)
下面通过一个完整的示例说明了如何通过原始的api接口一步一步的进行分片上传操作,如果用户需要做断点续传等高级操作,可以参考下面代码:
<?php
/**
* @param $s3Client S3Client实例
* @param $bucketName 桶名
* @param $objectName 对象名
* @param $body 文件指针
*/
function putObjectByRawApis($s3Client, $bucketName,$objectName,$body)
{
/**
* // 1. Create a new multipart upload and get the upload ID.
*/
ini_set('memory_limit', '500M');
try{
$uploadId = $s3Client->createMultipartUpload(['Bucket'=>$bucketName, 'Key'=>$objectName])['UploadId'];
} catch(\Aws\Exception\AwsException $e) {
printf(__FUNCTION__ . ": createMultipartUpload FAILED\n");
printf($e->getMessage() . "\n");
return;
}
print(__FUNCTION__ . ": createMultipartUpload OK" . "\n");
// 2. Upload the file in parts.
// 分片大小10M
$partSize = 5 * 1024 * 1024;
$parts = array();
$partNumber = 1;
while (!feof($body)) {
try{
$result = $s3Client->uploadPart(array(
'Bucket' => $bucketName,
'Key' => $objectName,
'UploadId' => $uploadId,
'PartNumber' => $partNumber,
'Body' => fread($body,$partSize),
));
echo $result . "\n";
} catch (\Aws\Exception\AwsException $exception){
printf(__FUNCTION__ . ": uploadPart FAILED\n");
printf($exception->getMessage() . "\n");
$s3Client->abortMultipartUpload(['Bucket'=>$bucketName,'Key'=>$objectName,'UploadId'=>$uploadId]);
return;
}
$parts[] = array(
'ETag' => $result['ETag'],
'PartNumber' => $partNumber++
);
echo "Uploading part {$partNumber}\n";
}
printf(__FUNCTION__ . ": uploadPart OK\n");
print_r($parts);
// 3. Complete multipart upload.
try{
$result = $s3Client->completeMultipartUpload(array(
'Bucket' => $bucketName,
'Key' => $objectName,
'UploadId' => $uploadId,
'MultipartUpload' => array('Parts' => $parts),
));
} catch (\Aws\Exception\AwsException $exception){
printf(__FUNCTION__ . ": uploadPart FAILED\n");
printf($exception->getMessage() . "\n");
$s3Client->abortMultipartUpload(['Bucket'=>$bucketName,'Key'=>$objectName,'UploadId'=>$uploadId]);
return;
}
printf(__FUNCTION__ . ": completeMultipartUpload OK\n");
}
Attention
- 上面程序一共分为三个步骤:1. initiate 2. uploadPart 3. complete
- UploadPart 方法要求除最后一个Part以外,其他的Part大小都要大于或等于16K。但是Upload Part接口并不会立即校验上传Part的大小(因为不知道是否为最后一块);只有当Complete Multipart Upload的时候才会校验。
- Part号码的范围是1~10000。如果超出这个范围,NOS将返回InvalidArgument的错误码。
- Part的大小为16K到100M
- 每次上传Part时都要把流定位到此次上传块开头所对应的位置。
- 分片上传任务初始化或上传部分分片后,可以使用abortMultipartUpload接口中止分片上传事件。当分片上传事件被中止后,就不能再使用这个Upload ID做任何操作,已经上传的分片数据也会被删除。
- 每次上传Part之后,NOS的返回结果会包含一个 PartETag 对象,它是上传块的ETag与块编号(PartNumber)的组合。在后续完成分片上传的步骤中会用到它,因此我们需要将其保存起来,然后在第三步complete的时候使用,具体操作参考上面代码。
5.3.3. 查看已经上传的分片¶
查看已经上传的分片可以罗列出指定Upload ID(InitiateMultipartUpload时获取)所属的所有已经上传成功的分片,您可以通过S3Client::listParts接口获取已经上传的分片,可以参考以下代码:
/**
* @param $s3Client S3Client实例
* @param $bucketName 桶名
* @param $objectName 对象名
* @param $uploadId 上传对象的唯一id
*/
function listParts($s3Client,$bucketName,$objectName,$uploadId){
try{
$MaxParts = 10;
$PartNumberMarker = 2;
$result = $s3Client->listParts(['Bucket'=>$bucketName,'Key'=>$objectName,'UploadId'=>$uploadId,
'PartNumberMarker'=>$PartNumberMarker,'MaxParts'=>$MaxParts]);
foreach($result['Parts'] as $partInfo){
print($partInfo['PartNumber'] . "\t" . $partInfo['Size'] . "\t" . $partInfo['ETag']);
}
} catch (\Aws\Exception\AwsException $exception){
echo $exception->getMessage() . "\n";
}
}
查看已经上传的分片可以指定以下参数:
参 数 | 描述 |
---|---|
MaxParts | 响应中的limit个数 类型:整型 |
PartNumberMarker | 分块号的界限,只有更大的分块号会被列出来。 类型:整型 |
5.3.4. 查看当前正在进行的分片上传任务¶
查看正在进行的分片上传任务可以罗列出正在进行,还未完成的分片上传任务,您可以通过S3Client::listMultipartUploads接口查看当前的上传任务,可以参考以下代码:
<?php
/**
* @param $s3Client S3Client实例
* @param $bucketName 桶名
*/
function listMultipartUploads($s3Client,$bucketName){
try{
$result = $s3Client->listMultipartUploads(['Bucket'=>$bucketName,'MaxUploads'=>2,'KeyMarker'=>'','Prefix'=>'']);
echo $result;
} catch (\Aws\Exception\AwsException $exception){
echo $exception->getMessage();
}
}
上述代码中使用的options参数如下:
参数值 | 描述 |
---|---|
KeyMarker | 指定某一uploads key,只有大于该kKeyMarker的才会被列出 |
MaxUploads | 最多返回MaxUploads条记录,默认1000 |
5.4. 设置文件元信息¶
文件元数据(object meta),是上传到NOS的文件属性描述信息:分为http标准属性和用户自定义元数据。文件元信息可以在各种上传方式(字符串上传、文件上传、分片上传)或copy文件时进行设置,元数据大小写不敏感。
5.4.1. 设定http header¶
NOS允许用户自定义http Header。http header相关信息请参考 RFC2616,几个常用的header说明如下:
名称 | 描述 | 默认值 |
---|---|---|
ContentType | 文件的MIME,定义文件的类型及网页编码,决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成,如果没有扩展名则填默认值 | application/octet-stream |
ContentDisposition | 指示MINME用户代理如何显示附加的文件,打开或下载,及文件名称 | 无 |
ContentLength | 上传的文件的长度,超过流/文件的长度会截断,不足为实际值 | 流/文件实际长度 |
Expires | 缓存过期时间,NOS未使用,格式是格林威治时间(GMT) | 无 |
CacheControl | 指定该Object被下载时的网页的缓存行为 | 无 |
下面的源代码实现了上传文件时设置Http header:
/**
* @param $s3Client S3Client实例
* @param $bucketName 桶名
* @param $objectName 对象名
* @param $Body 'mixed type: string|resource|\Guzzle\Http\EntityBodyInterface'
*/
function putObjectWithMetadata($s3Client,$bucketName,$objectName,$Body){
try{
$result = $s3Client->putObject(['Bucket'=>$bucketName,
'Key'=>$objectName,
'Body'=>$Body,
'Expires' => '2018-12-12 08:00:00',
'Content-Disposition' => 'attachment; filename="xxxxxx"',
]);
echo $result;
} catch (\Aws\Exception\AwsException $exception){
echo $exception->getMessage();
}
}
Attention
- 各种上传方式(包括字符串上传、文件上传、分片上传)都可以设置元数据信息,设置的方式相同
- 通过设置文件的Content-Type,可以修改文件的类型。
- 通过设置文件的Content-Disposition,可以控制文件的下载行为。
5.4.2. 用户自定义元数据¶
NOS支持用户自定义对象元数据,上传时可以指定对象自定义元数据,数据放在http头中传输,以x-aws-meta-开头,以下源代码实现对象自定义元数据上传:
<?php
/**
* @param $s3Client S3Client实例
* @param $bucketName 桶名
* @param $objectName 对象名
* @param $Body 'mixed type: string|resource|\Guzzle\Http\EntityBodyInterface'
*/
function putObjectWithUserMetadata($s3Client,$bucketName,$objectName,$Body){
try{
$metadata = ['bug'=>'yes',
'joker'=>'haha'];
$result = $s3Client->putObject(['Bucket'=>$bucketName,
'Key'=>$objectName,
'Body'=>$Body,
'Expires' => '2018-12-12 08:00:00',
'Content-Disposition' => 'attachment; filename="xxxxxx"',
'Metadata'=>$metadata
]);
echo $result;
} catch (\Aws\Exception\AwsException $exception){
echo $exception->getMessage();
}
}
Note
- 文件的元信息可以通过S3Client::headObject获取;
- user meta的名称大小写不敏感,例如设置为:’x-aws-meta-name’,读取名字为x-aws-meta-name的参数即可;