.NET SDK 手册
文件上传
在 NOS 中用户的基本操作单元是对象,亦可以理解为文件,NOS C# SDK 提供了丰富的上传接口,可以通过以下的方式上传文件:
- 流式上传
- 单块上传
- 分块上传
流式上传、单块上传最大为 100 M;分块上传除最后一块外,分块不能小于 16k,每一个分块不能大于 100M,最多能上传 10000 个分块,即分块上传的文件最大支持 1 T。
流式上传
您可以使用 NosClient.PutObject 上传一个一个 Stream 中的内容,具体实现如下:
// 初始化 NosClient
var nosClient = new NosClient(endpoint, accessKeyId, accessKeySecret);
/// <summary>
/// 流式上传
/// </summary>
/// <param name="bucket">桶名</param>
/// <param name="key">对象名</param>
/// <param name="content">需要上传的流</param>
public void PutObject(string bucket, string key, Stream content)
{
try
{
nosClient.PutObject(bucket, key, content);
Console.WriteLine("Put object:{0} succeeded", key);
}
catch (NosException ex)
{
Console.WriteLine("Failed with HTTPStatus: {0}; \nErrorCode: {1}; \nErrorMessage: {2}; \nRequestID:{3}; \nResource:{4}",
ex.StatusCode, ex.ErrorCode, ex.Message, ex.RequestId, ex.Resource);
}
catch (Exception ex)
{
Console.WriteLine("Failed with error info: {0}", ex.Message);
}
}
Attention
上传的流内容不超过 100M
单块上传
您可以使用 NosClient.PutObject 上传文件内容,具体实现如下:
// 初始化 NosClient
var nosClient = new NosClient(endpoint, accessKeyId, accessKeySecret);
/// <summary>
/// 上传文件
/// </summary>
/// <param name="bucket">桶名</param>
/// <param name="key">对象名</param>
/// <param name="fileToUpload">上传的文件</param>
public void PutObject(string bucket, string key, string fileToUpload)
{
try
{
nosClient.PutObject(bucket, key, fileToUpload);
Console.WriteLine("Put object:{0} succeeded", key);
}
catch (NosException ex)
{
Console.WriteLine("Failed with HTTPStatus: {0}; \nErrorCode: {1}; \nErrorMessage: {2}; \nRequestID:{3}; \nResource:{4}",
ex.StatusCode, ex.ErrorCode, ex.Message, ex.RequestId, ex.Resource);
}
catch (Exception ex)
{
Console.WriteLine("Failed with error info: {0}", ex.Message);
}
}
Attention
上传的文件内容不超过 100M
分块上传
除了通过 putObject 接口上传文件到 NOS 之外,NOS 还提供了另外一种上传模式-分块上传,用户可以在如下应用场景内(但不限于此),使用分块上传模式,如:
- 需支持断点上传
- 上传超过 100M 的文件
- 网络条件较差,经常和 NOS 服务器断开连接
- 上传文件之前无法确定文件的大小
分块上传一般流程如下所示:
- 初始化一个分块上传任务 (InitiateMultipartUpload)
- 上传分块 (UploadPart)
- 完成分块上传 (CompleteMultipartUpload) 或者取消分块上传 (AbortMultipartUpload)
初始化分块上传
您可以使用 NosClient.InitiateMultipartUpload 初始化分块上传,具体实现如下:
// 初始化 NosClient
var nosClient = new NosClient(endpoint, accessKeyId, accessKeySecret);
/// <summary>
/// 初始化分块上传
/// </summary>
/// <param name="bucket">桶名</param>
/// <param name="key">对象名</param>
public string InitiateMultipartUpload(String bucket, String key)
{
string uploadId = "";
try
{
var request = new InitiateMultipartUploadRequest(bucket, key);
var result = nosClient.InitiateMultipartUpload(request);
uploadId = result.UploadId;
Console.WriteLine("Init multipart upload succeeded");
}
catch (NosException ex)
{
Console.WriteLine("Failed with HTTPStatus: {0}; \nErrorCode: {1}; \nErrorMessage: {2}; \nRequestID:{3}; \nResource:{4}",
ex.StatusCode, ex.ErrorCode, ex.Message, ex.RequestId, ex.Resource);
}
catch (Exception ex)
{
Console.WriteLine("Failed with error info: {0}", ex.Message);
}
return uploadId;
}
上传分块
您可以使用 NosClient.UploadPart 上传分块,具体实现如下:
// 初始化 NosClient
var nosClient = new NosClient(endpoint, accessKeyId, accessKeySecret);
/// <summary>
/// 上传分块
/// </summary>
/// <param name="bucket">桶名</param>
/// <param name="key">对象名</param>
/// <param name="fileToUpload">上传的文件</param>
/// <param name="uploadId">分块上传的 ID</param>
/// <param name="partSize">分块大小</param>
public List<PartETag> UploadParts(String bucket, String key, String fileToUpload,
String uploadId, int partSize)
{
// 计算片个数
var fi = new FileInfo(fileToUpload);
var fileSize = fi.Length;
var partCount = fileSize / partSize;
if (fileSize % partSize != 0)
{
partCount++;
}
// 开始分片上传
var partETags = new List<PartETag>();
try
{
using (var fs = File.Open(fileToUpload, FileMode.Open))
{
for (var i = 0; i < partCount; i++)
{
var skipBytes = (long)partSize * i;
//定位到本次上传片应该开始的位置
fs.Seek(skipBytes, 0);
var size = (partSize < fileSize - skipBytes) ? partSize : (fileSize - skipBytes);
var request = new UploadPartRequest(bucket, objectName, uploadId)
{
Content = fs,
PartSize = size,
PartNumber = i + 1
};
var result = nosClient.UploadPart(request);
partETags.Add(result.PartETag);
}
}
Console.WriteLine("Put multipart upload succeeded");
}
catch (NosException ex)
{
Console.WriteLine("Failed with HTTPStatus: {0}; \nErrorCode: {1}; \nErrorMessage: {2}; \nRequestID:{3}; \nResource:{4}",
ex.StatusCode, ex.ErrorCode, ex.Message, ex.RequestId, ex.Resource);
}
catch (Exception ex)
{
Console.WriteLine("Failed with error info: {0}", ex.Message);
}
return partETags;
}
完成分块上传
您可以使用 NosClient.CompleteMultipartUpload 完成上传分块,具体实现如下:
// 初始化 NosClient
var nosClient = new NosClient(endpoint, accessKeyId, accessKeySecret);
/// <summary>
/// 完成分块上传
/// </summary>
/// <param name="bucket">桶名</param>
/// <param name="key">对象名</param>
/// <param name="uploadId">分块上传的 ID</param>
/// <param name="partETags">PartETag 对象,它是上传块的 ETag 与块编号(PartNumber)的组合</param>
public void CompleteUploadPart(String bucket, String key,
String uploadId, List<PartETag> partETags)
{
try
{
var completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucket, key, uploadId);
foreach (var partETag in partETags)
{
completeMultipartUploadRequest.PartETags.Add(partETag);
}
nosClient.CompleteMultipartUpload(completeMultipartUploadRequest);
Console.WriteLine("Complete Upload Part succeeded");
}
catch (NosException ex)
{
Console.WriteLine("Failed with HTTPStatus: {0}; \nErrorCode: {1}; \nErrorMessage: {2}; \nRequestID:{3}; \nResource:{4}",
ex.StatusCode, ex.ErrorCode, ex.Message, ex.RequestId, ex.Resource);
}
catch (Exception ex)
{
Console.WriteLine("Failed with error info: {0}", ex.Message);
}
}
分块上传示例
下面通过一个完整的示例说明了如何进行分片上传操作,可以参考下面代码:
// 初始化 NosClient
var nosClient = new NosClient(endpoint, accessKeyId, accessKeySecret);
/// <summary>
/// 分块上传示例
/// </summary>
/// <param name="bucket">桶名</param>
/// <param name="key">对象名</param>
/// <param name="fileToUpload">上传的文件</param>
public void UploadPart(string bucket, string key, string fileToUpload)
{
try
{
/*
* 初始化一个分块上传事件
* */
var initRequest = new InitiateMultipartUploadRequest(bucket, key);
var initResult = nosClient.InitiateMultipartUpload(initRequest);
/*
* 上传分块
* */
// 设置每块为 1M
const int partSize = 1024 * 1024 * 1;
var partFile = new FileInfo(fileToUpload);
var fileSize = partFile.Length;
// 计算分块数目
var partCount = fileSize / partSize;
if (fileSize % partSize != 0)
{
partCount++;
}
// 新建一个 List 保存每个分块上传后的 ETag 和 PartNumber
var partETags = new List<PartETag>();
//upload the file
using (var fs = new FileStream(partFile.FullName, FileMode.Open))
{
for (var i = 0; i < partCount; i++)
{
// 跳到每个分块的开头
long skipBytes = partSize * i;
fs.Position = skipBytes;
// 计算每个分块的大小
var size = partSize < partFile.Length - skipBytes? partSize : partFile.Length - skipBytes;
// 创建 UploadPartRequest,上传分块
var uploadPartRequest = new UploadPartRequest(bucket, key, initResult.UploadId)
{
Content = fs,
PartSize = size,
PartNumber = (i + 1)
};
var uploadPartResult = nosClient.UploadPart(uploadPartRequest);
// 将返回的 PartETag 保存到 List 中。
partETags.Add(uploadPartResult.PartETag);
}
/*
* 完成分块上传事件
* */
var completeRequest = new CompleteMultipartUploadRequest(bucket, key, initResult.UploadId);
foreach (var partETag in partETags)
{
completeRequest.PartETags.Add(partETag);
}
nosClient.CompleteMultipartUpload(completeRequest);
fs.Close();
}
Console.WriteLine("Upload Part succeeded");
}
catch (NosException ex)
{
Console.WriteLine("Failed with HTTPStatus: {0}; \nErrorCode: {1}; \nErrorMessage: {2}; \nRequestID:{3}; \nResource:{4}",
ex.StatusCode, ex.ErrorCode, ex.Message, ex.RequestId, ex.Resource);
}
catch (Exception ex)
{
Console.WriteLine("Failed with error info: {0}", ex.Message);
}
}
Note
上面程序一共分为三个步骤:
- initiate
- uploadPart
- complete
UploadPart 方法要求除最后一个 Part 以外,其他的 Part 大小都要大于或等于 16K。但是 Upload Part 接口并不会立即校验上传 Part 的大小(因为不知道是否为最后一块);只有当 Complete Multipart Upload 的时候才会校验。 Part 号码的范围是 1~10000。如果超出这个范围,NOS 将返回 InvalidArgument 的错误码。 分块上传除最后一块外,分块不能小于 16k,每一个分块不能大于 100M。 每次上传 Part 时都要把流定位到此次上传块开头所对应的位置。 分片上传任务初始化或上传部分分片后,可以使用 abortMultipartUpload 接口中止分片上传事件。当分片上传事件被中止后,就不能再使用这个 Upload ID 做任何操作,已经上传的分片数据也会被删除。 每次上传 Part 之后,NOS 的返回结果会包含一个 PartETag 对象,它是上传块的 ETag 与块编号(PartNumber)的组合。在后续完成分片上传的步骤中会用到它,因此我们需要将其保存起来,然后在第三步 complete 的时候使用,具体操作参考上面代码。
取消分块上传
您可以使用 NosClient.AbortMultipartUpload 取消上传事件,具体实现如下:
// 初始化 NosClient
var nosClient = new NosClient(endpoint, accessKeyId, accessKeySecret);
/// <summary>
/// 取消分块上传
/// </summary>
/// <param name="bucket">桶名</param>
/// <param name="key">对象名</param>
/// <param name="uploadId">分块上传的 ID</param>
public void AbortMultipartUpload(string bucket, string key, string uploadId)
{
try
{
var abortRequest = new AbortMultipartUploadRequest(bucket, key, uploadId);
nosClient.AbortMultipartUpload(abortRequest);
Console.WriteLine("Abort Multipart Upload succeeded");
}
catch (NosException ex)
{
Console.WriteLine("Failed with HTTPStatus: {0}; \nErrorCode: {1}; \nErrorMessage: {2}; \nRequestID:{3}; \nResource:{4}",
ex.StatusCode, ex.ErrorCode, ex.Message, ex.RequestId, ex.Resource);
}
catch (Exception ex)
{
Console.WriteLine("Failed with error info: {0}", ex.Message);
}
}
查看已经上传的分片
查看已经上传的分片可以罗列出指定 Upload ID(InitiateMultipartUpload 时获取) 所属的所有已经上传成功的分片,您可以通过 NosClient.ListParts 接口获取已经上传的分片,可以参考以下代码:
// 初始化 NosClient
var nosClient = new NosClient(endpoint, accessKeyId, accessKeySecret);
// <summary>
/// 列出已上传的分块
/// </summary>
/// <param name="bucket">桶名</param>
/// <param name="key">对象名</param>
/// <param name="uploadId">分块上传的 ID</param>
public void ListParts(string bucket, string key, string uploadId)
{
try
{
var listPartsRequest = new ListPartsRequest(bucket, key, uploadId);
var result = nosClient.ListParts(listPartsRequest);
Console.WriteLine("List parts succeeded");
foreach (var part in result.Parts)
{
Console.WriteLine(part.ToString());
}
}
catch (NosException ex)
{
Console.WriteLine("Failed with HTTPStatus: {0}; \nErrorCode: {1}; \nErrorMessage: {2}; \nRequestID:{3}; \nResource:{4}",
ex.StatusCode, ex.ErrorCode, ex.Message, ex.RequestId, ex.Resource);
}
catch (Exception ex)
{
Console.WriteLine("Failed with error info: {0}", ex.Message);
}
}
查看已经上传的分片可以指定以下参数:
参数 | 描述 |
---|---|
MaxParts | 响应中的 limit 个数 类型:整型 |
PartMumberMarker | 分块号的界限,只有更大的分块号会被列出来。类型:字符串 |
查看当前正在进行的分片上传任务
查看正在进行的分片上传任务可以罗列出正在进行,还未完成的分片上传任务,您可以通过 NosClient.ListMultipartUploads 接口当前的上传任务,可以参考以下代码:
// 初始化 NosClient
var nosClient = new NosClient(endpoint, accessKeyId, accessKeySecret);
// <summary>
/// 查看当前正在进行的分片上传任务
/// </summary>
/// <param name="bucket">桶名</param>
public void ListMultipartUploads(string bucket)
{
try
{
var listMultipartUploadsRequest = new ListMultipartUploadsRequest(bucket){
KeyMarker = "",
MaxUploads = 10
};
var result = nosClient.ListMultipartUploads(listMultipartUploadsRequest);
Console.WriteLine("List multi Uploads succeeded");
foreach (var mu in result.MultipartUploads)
{
Console.WriteLine("Key:{0}, UploadId:{1}", mu.Key , mu.UploadId);
}
}
catch (NosException ex)
{
Console.WriteLine("Failed with HTTPStatus: {0}; \nErrorCode: {1}; \nErrorMessage: {2}; \nRequestID:{3}; \nResource:{4}",
ex.StatusCode, ex.ErrorCode, ex.Message, ex.RequestId, ex.Resource);
}
catch (Exception ex)
{
Console.WriteLine("Failed with error info: {0}", ex.Message);
}
}
上述代码中使用的 options 参数如下:
参数值 | 描述 |
---|---|
KeyMarker | 指定某一 uploads key,只有大于该 KeyMarker 的才会被列出 |
MaxUploads | 最多返回 MaxUploads 条记录,默认 1000 |
设置文件元信息
文件元数据 (object meta),是上传到 NOS 的文件属性描述信息,分为 http 标准属性和用户自定义元数据。文件元信息可以在各种上传方式 (流式上传、单块上传、分块上传) 时进行设置。
设定 http header
NOS 允许用户自定义 http Header。http header 相关信息请参考 RFC2616,几个常用的 header 说明如下:
名称 | 描述 | 默认值 |
---|---|---|
Content-MD5 | 文件数据校验,设置了该值后 NOS 会启用文件内容 MD5 校验,把您提供的 MD5 与文件的 MD5 比较,不一致会抛出错误 | 无 |
Content-Type | 文件的 MIME,定义文件的类型及网页编码,决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据 Key 或文件名的扩展名生成,如果没有扩展名则填默认值 | application/octet-stream |
Content-Disposition | 指示 MINME 用户代理如何显示附加的文件,打开或下载,及文件名称 | 无 |
Content-Length | 上传的文件的长度,超过流/文件的长度会截断,不足为实际值 | 流/文件实际长度 |
Expires | 缓存过期时间,NOS 未使用,格式是格林威治时间(GMT) | 无 |
Cache-Control | 指定该 Object 被下载时的网页的缓存行为 | 无 |
下面的源代码实现了上传文件时设置 Http header:
// 初始化 NosClient
var nosClient = new NosClient(endpoint, accessKeyId, accessKeySecret);
/// <summary>
/// 上传文件
/// </summary>
/// <param name="bucket">桶名</param>
/// <param name="key">对象名</param>
/// <param name="fileToUpload">上传的文件</param>
public void putObject(string bucket, string key, string fileToUpload)
{
try
{
using (var fs = File.Open(fileToUpload, FileMode.Open))
{
var metadata = new ObjectMetadata
{
CacheControl = "no-cache",
ContentDisposition = "abc.zip",
ContentEncoding = "gzip",
};
//user metadata
metadata.UserMetadata.Add("x-nos-meta-my-metaMyKey1", "MyValue1");
metadata.UserMetadata.Add("x-nos-meta-my-metaMyKey2", "MyValue2");
nosClient.PutObject(bucket, key, fs, metadata);
Console.WriteLine("Put object succeeded");
}
}
catch (NosException ex)
{
Console.WriteLine("Failed with HTTPStatus: {0}; \nErrorCode: {1}; \nErrorMessage: {2}; \nRequestID:{3}; \nResource:{4}",
ex.StatusCode, ex.ErrorCode, ex.Message, ex.RequestId, ex.Resource);
}
catch (Exception ex)
{
Console.WriteLine("Failed with error info: {0}", ex.Message);
}
}
Attention
各种上传方式(包括流式上传、单块上传、分块上传)都可以设置元数据信息。流式上传设置元数据的方式与单块上传相同,分块上传在构造 ListMultipartUploadsRequest 对象时传入 ObjectMetadata 参数,如:ListMultipartUploadsRequest.ObjectMetadata; 文件的元信息可以通过 NosClient.GetObjectMetadata 获取;
user meta 的名称以”x-nos-meta-“开头,例如设置为:’x-nos-meta-name’,读取名字为 x-nos-meta-name 的参数即可;
上传时校验 MD5
下面的代码实现了上传时校验 MD5:
// 初始化 NosClient
var nosClient = new NosClient(endpoint, accessKeyId, accessKeySecret);
/// <summary>
/// 上传文件
/// </summary>
/// <param name="bucket">桶名</param>
/// <param name="key">对象名</param>
/// <param name="fileToUpload">上传的文件</param>
public void putObject(string bucket, string key, string fileToUpload)
{
try
{
string md5 = "";
using (var fs = File.Open(fileToUpload, FileMode.Open))
{
md5 = NosUtils.GetMD5FromStream(fs);
var metadata = new ObjectMetadata
{
ContentMD5 = md5
};
nosClient.PutObject(bucket, key, fs, metadata);
Console.WriteLine("Put object succeeded");
}
}
catch (NosException ex)
{
Console.WriteLine("Failed with HTTPStatus: {0}; \nErrorCode: {1}; \nErrorMessage: {2}; \nRequestID:{3}; \nResource:{4}",
ex.StatusCode, ex.ErrorCode, ex.Message, ex.RequestId, ex.Resource);
}
catch (Exception ex)
{
Console.WriteLine("Failed with error info: {0}", ex.Message);
}
}
Attention
校验 MD5 会有一定的性能损失