基于前文中修改过的RestSharp我们已经可以轻松的调用OSS中大部分API如,GetService (ListBucket),Put Bucket,Get Bucket ACL,Delete Bucket等,接下来就到OSS最重要的操作了——Put Object ,即文件上传。
本以为上传操作会非常简单,根据文档代码我们可以调用以下几个方法添加文件
IRestRequest AddFile (string name, string path); IRestRequest AddFile (string name, byte[] bytes, string fileName); IRestRequest AddFile (string name, byte[] bytes, string fileName, string contentType);
我们第一版的代码就像下面一样了:
public string PutObjectFromtFile(string bucket, string obj, string filePath) { var request = new RestRequest(); request.Resource = "/" + bucket + "/" + obj; request.Method = Method.PUT; request.AddFile(obj, filePath); var response = Execute(request); return response.Headers.Where(m => m.Name == "ETag").First().Value.ToString().ToLower().Substring(1, 32); }
进行测试无奈又爆出400错误。我们接着Fiddler抓包,提交数据包结构如下,有部分省略:
Host: storage.aliyun.com Content-Type: multipart/form-data; boundary=---------------------------122872775310383 Content-Length: 655 空行 -----------------------------122872775310383 Content-Disposition: form-data; name="txt"
通过查询得知:RestSharp的AddFile操作遵循了RFC1867协议,在上传文件时会添加额外的头文件进行分割。如同我们在浏览器中上传文件时必须在form中设置ENCTYPE必须为multipart/form-data。具体参考:通过解析HTTP协议自己实现文件上传。但是在阿里OSS服务中规定,PUT Object 操作使用PUT方法,并将文件以文件流方式添加到HTTP的body中。
既然知道了原因我们接下来就进行修改,我们还是先看看Google讨论组上的有没有解决方法,找到一篇有用的帖子PUT's dont honor the RequestFormat ,接下来就对RestSharp大刀阔斧的改造了。
改造思路如下,首先为添加一个属性,用来标识是否上传原始文件流;然后找到构造multipart协议的代码,根据属性来确定是否上传原始文件。
我么先找到了添加文件的位置位于Http.cs文件中:
private void WriteMultipartFormData(Stream requestStream) { foreach (var param in Parameters) { WriteStringTo(requestStream, GetMultipartFormData(param)); } foreach (var file in Files) { // Add just the first part of this param, since we will write the file data directly to the Stream WriteStringTo(requestStream, GetMultipartFileHeader(file)); // Write the file data directly to the Stream, rather than serializing it to a string. file.Writer(requestStream); WriteStringTo(requestStream, _lineBreak); } WriteStringTo(requestStream, GetMultipartFooter()); }
我们Http类实现了IHttp接口,我们添加UploadRaw属性在IHttp接口中,并将上面代码改为:
private void WriteMultipartFormData(Stream requestStream) { if (!UploadRaw) { foreach (var param in Parameters) { WriteStringTo(requestStream, GetMultipartFormData(param)); } foreach (var file in Files) { // Add just the first part of this param, since we will write the file data directly to the Stream WriteStringTo(requestStream, GetMultipartFileHeader(file)); // Write the file data directly to the Stream, rather than serializing it to a string. file.Writer(requestStream); WriteStringTo(requestStream, _lineBreak); } WriteStringTo(requestStream, GetMultipartFooter()); } else { //写入原始文件流 var file = Files.First(); file.Writer(requestStream); } }
初始化Http类的代码在RestClient类中
private void ConfigureHttp(IRestRequest request, IHttp http)
代码太多,就不粘贴了,从上面方法中我们的看出来http的属性实际是根据request的值构造的。因此我们还需要在IRestRequest添加一个属性,否则没有办法赋值,同样添加UploadRaw属性。并在ConfigureHttp方法中添加一行 http.UploadRaw=request.UploadRaw即可。
改造完毕看看我们现在的文件上传方法:
public string PutObjectFromtFile(string bucket, string obj, string filePath) { var request = new RestRequest(); request.Resource = "/" + bucket + "/" + obj; request.Method = Method.PUT; request.AddFile(obj, filePath); //关键在这里 request.UploadRaw = true; var response = Execute(request); return response.Headers.Where(m => m.Name == "ETag").First().Value.ToString().ToLower().Substring(1, 32); }
至此,我们OSS API的全部操作都可以顺利的完成了。希望对用到RestSharp的朋友有帮助。