《打造一个网站或者其他网络应用的文件管理接口(WebApi)第二章“多文件上传”》
========================================================
作者:qiujuer
开源库:Genius-Android
转载请注明出处:http://blog.csdn.net/qiujuer/article/details/41675299
========================================================
History
In This
既然访问文件接口有了,怎么能少的了文件的上传接口呢!
既然是文件上传接口那就肯定要来一个猛一点的接口--多文件上传
CodeTime
改动
进入正题前先来看看本次改动的文件有哪些:
可以看见一共有4个文件进行了改动,其中Home与ResourceApi是添加了方法,Model/Resource是新增,View/Upload也是新增。
Model部分
为了返回数据简单方便,所以New 了一个类 Resource.cs
namespace WebResource.Models { public class Resource { public string Id { get; set; } public string Type { get; set; } } }
If Easy?
ResourceApi 部分
然后来看看咱们的ResourceApi类:
namespace WebResource.Controllers { [RoutePrefix("Resource")] public class ResourceApiController : ApiController { private static readonly long MEMORY_SIZE = 64 * 1024 * 1024; private static readonly string ROOT_PATH = HttpContext.Current.Server.MapPath("~/App_Data/"); [HttpGet] [Route("{Id}")] public async Task<HttpResponseMessage> Get(string Id) { // 进入时判断当前请求中是否含有 ETag 标识,如果有就返回使用浏览器缓存 // Return 304 var tag = Request.Headers.IfNoneMatch.FirstOrDefault(); if (Request.Headers.IfModifiedSince.HasValue && tag != null && tag.Tag.Length > 0) return new HttpResponseMessage(HttpStatusCode.NotModified); // 进行模拟 App_Data/Image/{id}.png // 打开找到文件 FileInfo info = new FileInfo(Path.Combine(ROOT_PATH, "Image", Id + ".png")); if (!info.Exists) return new HttpResponseMessage(HttpStatusCode.BadRequest); FileStream file = null; try { // 打开文件 file = new FileStream(info.FullName, FileMode.Open, FileAccess.Read, FileShare.Read); HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); // 在浏览器中显示 inline ContentDispositionHeaderValue disposition = new ContentDispositionHeaderValue("inline"); // 写入文件基本信息 disposition.FileName = file.Name; disposition.Name = file.Name; disposition.Size = file.Length; // 判断是否大于64Md,如果大于就采用分段流返回,否则直接返回 if (file.Length < MEMORY_SIZE) { //Copy To Memory And Close. byte[] bytes = new byte[file.Length]; await file.ReadAsync(bytes, 0, (int)file.Length); file.Close(); MemoryStream ms = new MemoryStream(bytes); result.Content = new ByteArrayContent(ms.ToArray()); } else { result.Content = new StreamContent(file); } // 写入文件类型,这里是图片png result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png"); result.Content.Headers.ContentDisposition = disposition; // 设置缓存信息,该部分可以没有,该部分主要是用于与开始部分结合以便浏览器使用304缓存 // Set Cache result.Content.Headers.Expires = new DateTimeOffset(DateTime.Now).AddHours(1); // 这里应该写入文件的存储日期 result.Content.Headers.LastModified = new DateTimeOffset(DateTime.Now); result.Headers.CacheControl = new CacheControlHeaderValue() { Public = true, MaxAge = TimeSpan.FromHours(1) }; // 设置Etag,这里就简单采用 Id result.Headers.ETag = new EntityTagHeaderValue(string.Format("\"{0}\"", Id)); // 返回请求 return result; } catch { if (file != null) { file.Close(); } } return new HttpResponseMessage(HttpStatusCode.BadRequest); } [HttpPost] [Route("Upload")] [ResponseType(typeof(Resource))] public async Task<IHttpActionResult> Post() { List<Resource> resources = new List<Resource>(); // multipart/form-data // 采用MultipartMemoryStreamProvider var provider = new MultipartMemoryStreamProvider(); //读取文件数据 await Request.Content.ReadAsMultipartAsync(provider); foreach (var item in provider.Contents) { // 判断是否是文件 if (item.Headers.ContentDisposition.FileName != null) { //获取到流 var ms = item.ReadAsStreamAsync().Result; //进行流操作 using (var br = new BinaryReader(ms)) { if (ms.Length <= 0) break; //读取文件内容到内存中 var data = br.ReadBytes((int)ms.Length); //Create //当前时间作为ID Resource resource = new Resource() { Id = DateTime.Now.ToString("yyyyMMddHHmmssffff", DateTimeFormatInfo.InvariantInfo) }; //Info FileInfo info = new FileInfo(item.Headers.ContentDisposition.FileName.Replace("\"", "")); //文件类型 resource.Type = info.Extension.Substring(1).ToLower(); //Write try { //文件存储地址 string dirPath = Path.Combine(ROOT_PATH); if (!Directory.Exists(dirPath)) { Directory.CreateDirectory(dirPath); } File.WriteAllBytes(Path.Combine(dirPath, resource.Id), data); resources.Add(resource); } catch { } } } } //返回 if (resources.Count == 0) return BadRequest(); else if (resources.Count == 1) return Ok(resources.FirstOrDefault()); else return Ok(resources); } } }
与上一章比较看来,只是增加了一个方法 Post ,而后将其重定向为Resource/Upload
其中主要干了些什么我都在方法中注明了;应该是足够简单的。
现在运行一下,看看我们的Api是怎样的:
下面我们来调用它试试。
HomeController部分
修改HomeController 添加一个Upload 方法:
namespace WebResource.Controllers { public class HomeController : Controller { public ActionResult Index() { ViewBag.Title = "Home Page"; return View(); } public ActionResult Upload() { return View(); } } }
Upload.cshtml部分
而后在方法上点击右键添加视图,然后进入View/Home/Upload.cshtml 视图中修改为:
@{ ViewBag.Title = "Upload"; } <h2>Upload</h2> <div id="body"> <h1>多文件上传模式</h1> <section class="main-content clear-fix"> <form name="form1" method="post" enctype="multipart/form-data" action="/Resource/Upload"> <fieldset> <legend>File Upload Example</legend> <div> <label for="caption">File1</label> <input name="file1" type="file" /> </div> <div> <label for="image1">File2</label> <input name="file2" type="file" /> </div> <div> <input type="submit" value="Submit" /> </div> </fieldset> </form> </section> </div>
在该视图中,我们建立了一个 Form 表单,然后指定为 Post 模式;同时指定
enctype="multipart/form-data" action="/Resource/Upload"
RunTime
写完了代码当然是调试运行喽!
运行 localhost:60586/Home/Upload
这里我们添加文件,还是使用上一章中的两种图片吧:
运行后:
当然,这里是因为我的浏览器是谷歌浏览器,所以返回的是 XML 数据,如果你的事IE浏览器那么应该返回的是Json 文件。
WebApi会根据请求返回不同的数据。
可以看到,现在 App_Data文件夹下,多了两个文件了;不过这两个文件是没有加上文件类型的;你可以手动给他加上个.png 然后打开看看是不是那两张图片。
END
OK,这一章就到此为止了!
资源文件
下一章
下一章将会把上传与下载查看两个方法相互结合,搭配起来;同时将会结合数据进行辅助存储文件信息。
同时,会讲如何避免文件重复上传的问题。
========================================================
作者:qiujuer
开源库:Genius-Android
转载请注明出处:http://blog.csdn.net/qiujuer/article/details/41675299
========================================================