using System;
using System.Collections.Generic;
using System.Text;
using COSXML.Common;
using System.IO;
using COSXML.Utils;
using COSXML.CosException;
using COSXML.Auth;
using COSXML.Log;
using COSXML.Network;
namespace COSXML.Model.Object
{
///
/// 使用者用表单的形式将文件(Object)上传至指定 Bucket 中.
///
///
public sealed class PostObjectRequest : ObjectRequest
{
///
/// 表单字段
///
///
private FormStruct formStruct;
private PostObjectRequest(string bucket, string key)
: base(bucket, "/")
{
this.method = CosRequestMethod.POST;
formStruct = new FormStruct();
formStruct.key = key;
this.SetHeader(CosRequestHeaderKey.CONTENT_TYPE, "multipart/form-data; boundary=" + MultipartRequestBody.BOUNDARY);
this.needMD5 = false;
}
///
/// 上传文件
///
///
///
///
public PostObjectRequest(string bucket, string key, string srcPath)
: this(bucket, key, srcPath, -1L, -1L)
{
}
///
/// 上传文件的指定部分
///
///
///
///
/// 指定文件内容的起始位置
/// 指定文件内容的大小
public PostObjectRequest(string bucket, string key, string srcPath, long fileOffset, long sendContentLength)
: this(bucket, key)
{
formStruct.srcPath = srcPath;
formStruct.fileOffset = fileOffset < 0 ? 0 : fileOffset;
formStruct.contentLength = sendContentLength < 0L ? -1L : sendContentLength;
}
///
/// 上传data数据
///
///
///
///
public PostObjectRequest(string bucket, string key, byte[] data)
: this(bucket, key)
{
formStruct.data = data;
}
///
/// 设置进度回调
///
///
public void SetCosProgressCallback(COSXML.Callback.OnProgressCallback progressCallback)
{
formStruct.progressCallback = progressCallback;
}
///
/// 定义 Object 的 acl 属性。有效值:private,public-read-write,public-read;默认值:private
///
///
///
public void SetCosACL(CosACL cosACL)
{
SetCosACL(EnumUtils.GetValue(cosACL));
}
///
/// 定义 Object 的 acl 属性。有效值:private,public-read-write,public-read;默认值:private
///
///
///
public void SetCosACL(string cosACL)
{
if (cosACL != null)
{
formStruct.acl = cosACL;
}
}
///
/// 设置对象的 cacheControl
///
///
public void SetCacheControl(string cacheControl)
{
SetHeader("Cache-Control", cacheControl);
}
///
/// 设置对象的contentType
///
///
public void SetContentType(string contentType)
{
SetHeader("Content-Type", contentType);
}
///
/// 设置对象的contentDisposition
///
///
public void SetContentDisposition(string contentDisposition)
{
SetHeader("Content-Disposition", contentDisposition);
}
///
/// 设置对象的contentEncoding
///
///
public void SetContentEncoding(string contentEncoding)
{
SetHeader("Content-Encoding", contentEncoding);
}
///
/// 设置对象 Expire
///
///
public void SetExpires(string expires)
{
SetHeader("Expires", expires);
}
///
/// 设置对象header属性
///
///
///
public void SetHeader(string key, string value)
{
try
{
formStruct.headers.Add(key, value);
}
catch (ArgumentException)
{
formStruct.headers[key] = value;
}
}
///
/// 设置对象自定义的header属性
///
///
///
public void SetCustomerHeader(string key, string value)
{
try
{
formStruct.customHeaders.Add(key, value);
}
catch (ArgumentException)
{
formStruct.customHeaders[key] = value;
}
}
///
/// 设置对象的存储类型
///
///
///
public void SetCosStorageClass(string cosStorageClass)
{
formStruct.xCosStorageClass = cosStorageClass;
}
///
/// 最大上传速度,单位是 bit/s
///
///
public void LimitTraffic(long rate)
{
formStruct.xCOSTrafficLimit = rate.ToString();
}
///
/// 若设置优先生效,返回 303 并提供 Location 头部,
/// 会在 URL 尾部加上 bucket={bucket}<key={key}>etag={%22etag%22} 参数。
///
///
public void SetSuccessActionRedirect(string redirectHost)
{
formStruct.successActionRedirect = redirectHost;
}
///
/// successHttpCode can be 200, 201, 204, default value 204
/// 若填写 success_action_redirect 则会略此设置。
///
///
public void SetSuccessActionStatus(int successHttpCode)
{
formStruct.successActionStatus = successHttpCode.ToString();
}
///
/// 用于做请求检查,如果请求的内容和 Policy 指定的条件不符,返回 403 AccessDenied。
///
///
///
public void SetPolicy(Policy policy)
{
formStruct.policy = policy;
}
public override void CheckParameters()
{
formStruct.CheckParameter();
base.CheckParameters();
}
public override CosXmlSignSourceProvider GetSignSourceProvider()
{
var signSourceProvider = base.GetSignSourceProvider();
if (signSourceProvider != null)
{
signSourceProvider.RemoveHeaderKey("content-type");
signSourceProvider.RemoveHeaderKey("content-length");
signSourceProvider.RemoveHeaderKey("content-md5");
signSourceProvider.onGetSign = delegate (Request request, string sign)
{
//添加参数 sign
((MultipartRequestBody)request.Body).AddParameter("Signature", sign);
};
}
return signSourceProvider;
}
public override RequestBody GetRequestBody()
{
MultipartRequestBody requestBody = new MultipartRequestBody();
requestBody.AddParamters(formStruct.GetFormParameters());
if (formStruct.data != null)
{
requestBody.AddData(formStruct.data, "file", "tmp");
}
else if (formStruct.srcPath != null)
{
FileInfo fileInfo = new FileInfo(this.formStruct.srcPath);
string fileName = fileInfo.Name;
if (formStruct.contentLength == -1L || formStruct.contentLength + formStruct.fileOffset > fileInfo.Length)
{
formStruct.contentLength = fileInfo.Length - formStruct.fileOffset;
}
requestBody.AddData(formStruct.srcPath, formStruct.fileOffset, formStruct.contentLength, "file", fileName);
}
requestBody.ProgressCallback = formStruct.progressCallback;
return requestBody;
}
private class FormStruct
{
///
/// 对象的ACL
///
public string acl;
///
/// 对象的header元数据
///
public Dictionary headers;
///
/// 上传后的文件名,使用 ${filename} 则会进行替换。
/// 例如a/b/${filename},上传文件 a1.txt,那么最终的上传路径就是 a/b/a1.txt
///
public string key;
///
/// 若设置优先生效,返回 303 并提供 Location 头部
///
public string successActionRedirect;
///
/// 可选 200,201,204 默认返回 204。若填写 success_action_redirect 则会略此设置。
///
public string successActionStatus;
///
/// 对象的自定义元数据
///
public Dictionary customHeaders;
///
/// 对象存储类型
///
public string xCosStorageClass;
///
/// 速度限制
///
public string xCOSTrafficLimit;
///
/// 请求检查策略
///
///
public Policy policy;
///
/// 上传文件的本地路径
///
public string srcPath;
///
/// 上传文件指定起始位置
///
public long fileOffset = 0L;
///
/// 上传文件指定内容大小
///
public long contentLength = -1L;
///
/// 上传data数据
///
public byte[] data;
///
/// 上传回调
///
public COSXML.Callback.OnProgressCallback progressCallback;
public FormStruct()
{
headers = new Dictionary();
customHeaders = new Dictionary();
}
public Dictionary GetFormParameters()
{
Dictionary formParameters = new Dictionary();
if (acl != null)
{
formParameters.Add("Acl", acl);
}
foreach (KeyValuePair pair in headers)
{
formParameters.Add(pair.Key, pair.Value);
}
formParameters.Add("key", key);
if (successActionRedirect != null)
{
formParameters.Add("success_action_redirect", successActionRedirect);
}
if (successActionStatus != null)
{
formParameters.Add("success_action_status", successActionStatus);
}
foreach (KeyValuePair pair in customHeaders)
{
formParameters.Add(pair.Key, pair.Value);
}
if (xCosStorageClass != null)
{
formParameters.Add("x-cos-storage-class", xCosStorageClass);
}
if (xCOSTrafficLimit != null)
{
formParameters.Add(CosRequestHeaderKey.X_COS_TRAFFIC_LIMIT, xCOSTrafficLimit);
}
if (policy != null)
{
formParameters.Add("policy", DigestUtils.GetBase64(policy.Content(), Encoding.UTF8));
}
return formParameters;
}
public void CheckParameter()
{
if (String.IsNullOrEmpty(key))
{
throw new CosClientException((int)CosClientError.InvalidArgument, "FormStruct.key(null or empty) is invalid");
}
if (srcPath == null && data == null)
{
throw new CosClientException((int)CosClientError.InvalidArgument, "data source = null");
}
if (srcPath != null)
{
if (!File.Exists(srcPath))
{
throw new CosClientException((int)CosClientError.InvalidArgument, "srcPath not exist");
}
}
}
}
public class Policy
{
///
/// 过期时间
///
private string expiration;
///
/// 检查条件
///
private StringBuilder conditions = new StringBuilder();
public void SetExpiration(long endTimeMills)
{
this.expiration = TimeUtils_QCloud.GetFormatTime("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", endTimeMills, TimeUnit.Milliseconds);
}
public void AddConditions(string key, string value, bool isPrefixMatch)
{
if (conditions.Length > 0)
{
conditions.Append(',');
}
if (isPrefixMatch)
{
conditions.Append('[')
.Append("\"starts-with\"")
.Append(",\"")
.Append(key)
.Append("\",\"")
.Append(value)
.Append("\"]");
}
else
{
conditions.Append("{\"")
.Append(key)
.Append("\":\"")
.Append(value)
.Append("\"}");
}
}
public void AddContentConditions(int start, int end)
{
if (conditions.Length > 0)
{
conditions.Append(',');
}
conditions.Append('[')
.Append("\"content-length-range\"")
.Append(',')
.Append(start)
.Append(',')
.Append(end)
.Append(']');
}
public string Content()
{
StringBuilder content = new StringBuilder();
content.Append('{');
if (expiration != null)
{
content.Append(String.Format("\"expiration\":\"{0}\"", expiration));
}
if (conditions.Length > 0)
{
if (expiration != null)
{
content.Append(",");
}
content.Append(String.Format("\"conditions\":[{0}]", conditions));
}
content.Append('}');
return content.ToString();
}
}
}
}