本文主要介绍用 asp.net webapi 做文件服务器(包含上传和读取功能)
1、上文已经介绍了 如何使用 webapi 接受 uploadify 上传的文件(multipart/form-data),
本文在上文的基础上进行高级开发
2、限制只有 HandlerAll 接口支持跨域, 删除 webconfig 文件里面所有接口支持跨域的返回头
在 HandlerAll 接口开始处返回
HttpResponseMessage response = Request.CreateResponse(); response.Headers.Add("Access-Control-Allow-Origin", "*"); response.Headers.Add("Access-Control-Allow-Methods", "OPTIONS,POST"); response.Headers.Add("Access-Control-Allow-Headers", "Content-Type,soapaction");
3、自定义文件保存路径 ,参考这篇文章 http://blog.csdn.net/starfd/article/details/48652871
说明下主要修改的地方,原来是通过 MultipartFormDataStreamProvider 接的,这个类里面 有个方法 GetStream,是实际处理上传文件的关键
public class MultipartFormDataStreamProvider : MultipartFileStreamProvider { // Fields private NameValueCollection _formData; private Collection<bool> _isFormData; // Methods public MultipartFormDataStreamProvider(string rootPath) : base(rootPath) { this._formData = HttpValueCollection.Create(); this._isFormData = new Collection<bool>(); } public MultipartFormDataStreamProvider(string rootPath, int bufferSize) : base(rootPath, bufferSize) { this._formData = HttpValueCollection.Create(); this._isFormData = new Collection<bool>(); } public override Task ExecutePostProcessingAsync() => TaskHelpers.Iterate(base.Contents.Where<HttpContent>(((Func<HttpContent, int, bool>) ((content, index) => this._isFormData[index]))).Select<HttpContent, Task>(delegate (HttpContent formContent) { ContentDispositionHeaderValue contentDisposition = formContent.Headers.ContentDisposition; string formFieldName = FormattingUtilities.UnquoteToken(contentDisposition.Name) ?? string.Empty; bool runSynchronously = true; return formContent.ReadAsStringAsync().Then<string>(delegate (string formFieldValue) { this.FormData.Add(formFieldName, formFieldValue); }, new CancellationToken(), runSynchronously); }), new CancellationToken(), true); public override Stream GetStream(HttpContent parent, HttpContentHeaders headers) { if (parent == null) { throw Error.ArgumentNull("parent"); } if (headers == null) { throw Error.ArgumentNull("headers"); } ContentDispositionHeaderValue contentDisposition = headers.ContentDisposition; if (contentDisposition == null) { throw Error.InvalidOperation(Resources.MultipartFormDataStreamProviderNoContentDisposition, new object[] { "Content-Disposition" }); } if (!string.IsNullOrEmpty(contentDisposition.FileName)) { this._isFormData.Add(false); return base.GetStream(parent, headers); } this._isFormData.Add(true); return new MemoryStream(); } // Properties public NameValueCollection FormData => this._formData; }
再看下他的父类 MultipartFileStreamProvider,一目了然了, MultipartFormDataStreamProvider从名字看就知道是处理整个表单提交里所有正文的,而 MultipartFileStreamProvider 是用来专门处理文件的,因此看文件怎么保存的,到
MultipartFileStreamProvider 类里面 GetStream 就能知道怎么处理的了
public class MultipartFileStreamProvider : MultipartStreamProvider { // Fields private int _bufferSize; private Collection<MultipartFileData> _fileData; private string _rootPath; private const int DefaultBufferSize = 0x1000; private const int MinBufferSize = 1; // Methods public MultipartFileStreamProvider(string rootPath) : this(rootPath, 0x1000) { } public MultipartFileStreamProvider(string rootPath, int bufferSize) { this._bufferSize = 0x1000; this._fileData = new Collection<MultipartFileData>(); if (rootPath == null) { throw Error.ArgumentNull("rootPath"); } if (bufferSize < 1) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("bufferSize", bufferSize, 1); } this._rootPath = Path.GetFullPath(rootPath); this._bufferSize = bufferSize; } public virtual string GetLocalFileName(HttpContentHeaders headers) { if (headers == null) { throw Error.ArgumentNull("headers"); } return string.Format(CultureInfo.InvariantCulture, "BodyPart_{0}", new object[] { Guid.NewGuid() }); } public override Stream GetStream(HttpContent parent, HttpContentHeaders headers) { string str; if (parent == null) { throw Error.ArgumentNull("parent"); } if (headers == null) { throw Error.ArgumentNull("headers"); } try { string localFileName = this.GetLocalFileName(headers); str = Path.Combine(this._rootPath, Path.GetFileName(localFileName)); } catch (Exception exception) { throw Error.InvalidOperation(exception, Resources.MultipartStreamProviderInvalidLocalFileName, new object[0]); } MultipartFileData item = new MultipartFileData(headers, str); this._fileData.Add(item); return File.Create(str, this._bufferSize, FileOptions.Asynchronous); } // Properties protected int BufferSize => this._bufferSize; public Collection<MultipartFileData> FileData => this._fileData; protected string RootPath => this._rootPath; }
File.Create(str, this._bufferSize, FileOptions.Asynchronous); 这句是关键,因此只需要在这边不保存就可以了
4、上传文件后保存带有自定义水印的文件,打水印的原理很简单,就是往原图片快印你的水印图片即可,这里介绍水印图片透明度控制的原理(扩展)
c# 有个 ImageAttributes 类,参考网上代码 都有个 5*5 的矩阵,矩阵原理 参考 http://tieba.baidu.com/p/1730549463
5、控制图片质量(压缩图片) 原理(扩展) 参考 http://www.jb51.net/article/44538.htm、http://www.2cto.com/kf/201007/52380.html
原理:图片质量通常都是针对 jpg 图片说的,因为 jpg 图片有一个专门控制图片质量的参数,具体的我也没有详细了解
6、mine 参考手册地址 http://www.w3school.com.cn/media/media_mimeref.asp(非全)
7、分享一个 base64 对称加密的算法 参考 http://bbs.csdn.net/topics/360042162
8、base64加密后的字符串含有 + 和 / , 无法 在 get 和 post(http协议) 参数上使用的解决方案
输出时替换 + => - , / => = , 处理时再替换回来 解密
9、wepapi 动态接口输出文件无法被 cdn 缓存的解决方案 参考 http://www.cnblogs.com/losbyday/p/5843960.html、http://www.cnblogs.com/softidea/p/5986339.html
原因: cache-control 控制缓存的优先级比 expires 的高, 通过监视 接口输出 和 静态文件输出 , 可以知道原因 (上面是静态,下面是api接口输出文件)
10、iis 禁止输出 ETag , 可以提升性能 参考 http://www.cnblogs.com/dudu/p/iis-remove-response-readers.html
欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。