在前端与后端交互,ajax是最常用的方式,而后台的响应也有很多方式,在asp.net中有一般处理程序和MVC的Controller响应的方式。在使用controller来响应请求时,可以通过路由配置来定位到具体的方法,这样代码清晰,逻辑分明,但前端传到后端的参数一般是通过函数的参数来接收的,而对于文件这样较大的数据,不太方便接收,虽然有类似HttpPostedFileBase的类作为参数,但使用ajax来与其交互时变的不易处理,那有什么其他的方法呢?
有的!
上传文件由于是数据量比较大的传输,所以使用ajax的post方法,在post的时候将数据放到FormData中,如果还有其他的参数要传输,为了便于后端的接收,我们可以将其放到URL的问号后面,而后端使用函数的参数来接收,只要标识为FromUri就可以了。文件数据传输时必然是要用multipart的方式来传的,后端也自然的要以这种方式来接收。由于Post文件数据是分断的,为了保证接收,我们后端使用Task来接收。我们来看具体的代码。
前端代码
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>上传</title> <meta charset="utf-8" /> <link href="stylesheets/css.css" rel="stylesheet" /> <script src="scripts/zepto.min.js"></script> </head> <body> <div class="all"> <div id="ajaxWait" class="mask"> <img src="images/loading.gif" /> </div> <div class="head"> </div> <div class="body"> <div class="body_style"> <div class="upload-container"> <div style="height:43px;"> <div class="upload-wrap"> <input type="file" id="file1" class="upload-pic" > <span class="upload-text"> 选择图片1 </span> </div> </div> <div class="upload-preview-wrap"> <img id="fileStylePreview" class="upload-preview-img" /> </div> </div> <div class="upload-container"> <div style="height:43px;"> <div class="upload-wrap"> <input type="file" id="file2" class="upload-pic" > <span class="upload-text"> 选择图片2 </span> </div> </div> <div class="upload-preview-wrap"> <img id="fileWearPreview" class="upload-preview-img" /> </div> </div> <div style="margin-bottom:5px;margin-top:5px;"> <label>类型</label> <select id="clothesType" style="90px;height:30px;"> <option value="Type1" selected="selected">类型1</option> <option value="Type1">类型2</option> </select> </div> <div> <input type="button" value="上传" id="btnUpload" class="upload-wrap " /> </div> </div> </div> <div class="footer"></div> </div> </body> </html> <script type="text/javascript"> $(document).ready(function () { $("#btnUpload").click(function () { $("#ajaxWait").show(); var formData = new FormData(); formData.append("file1", document.getElementById("file1").files[0]); formData.append("file2", document.getElementById("file2").files[0]); var type = $("#clothesType").val(); $.ajax({ url: "/api/MyFiles/Upload?type=" + type, type: "POST", data: formData, contentType: false,//必须false才会自动加上正确的Content-Type processData: false,//必须false才会避开jQuery对 formdata 的默认处理.XMLHttpRequest会对 formdata 进行正确的处理. success: function (data) { $("#ajaxWait").hide(); if (data.Status == 0) { alert("上传成功!"); } else { alert(data.Message); } }, error: function (data) { $("#ajaxWait").hide(); alert("上传失败!" + data.Message); } }); }); function fileInputChang(obj) { var id = obj.target.id; var files = document.getElementById(id).files; var img = document.getElementById(id + "Preview"); if (!files || files.length <= 0) { img.src = ""; return; } var file; if (files && files.length > 0) { // 获取目前上传的文件 file = files[0]; // 那么我们可以做一下诸如文件大小校验的动作 if (file.size > 1024 * 1024 * 2) { alert('图片大小不能超过 2MB!'); return false; } // 图片预览 function 1 if (window['FileReader']) { var reader = new FileReader(); } else { alert("您的设备不支持图片预览功能,如需该功能请升级您的设备!"); } var reader = new FileReader(); var imageType = /^image//; //是否是图片 if (!imageType.test(file.type)) { alert("请选择图片!"); return; } //读取完成 reader.onload = function (e) { //图片路径设置为读取的图片 img.src = this.result; }; reader.readAsDataURL(file); } } $("#fileStyle").change(fileInputChang); $("#fileWear").change(fileInputChang); }); </script>后端代码
using System; using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using System.Web; using System.Web.Http; namespace UploadDemo.Controllers { public class MyFilesController : ApiController { #region Upload [HttpPost] public BaseDataPackage<string> Upload([FromUri]TryClothesTypeEnum type) { var result = new BaseDataPackage<string>(); if (!Request.Content.IsMimeMultipartContent()) { result.Status = StatusCode.Fail; result.Message = "Data Invalid"; result.Data = null; return result; } var server = HttpContext.Current.Server; string webRoot = "~/Upload/" + type.ToString(); string physicsRoot = server.MapPath(webRoot);//保存上传文件的目录 if (!Directory.Exists(physicsRoot)) { Directory.CreateDirectory(physicsRoot); } var provider = new RenameMultipartFormDataStreamProvider(physicsRoot); IEnumerable<HttpContent> parts = null; Task.Factory .StartNew(() => { parts = Request.Content.ReadAsMultipartAsync(provider).Result.Contents; }, CancellationToken.None, TaskCreationOptions.LongRunning, //确保线程分离 TaskScheduler.Default) .Wait(); try { result.Status = StatusCode.OK; result.Message = "OK"; result.Data = null; } catch (Exception ex) { result.Status = StatusCode.Fail; result.Message = ex.Message; result.Data = null; } return result; } #region RenameMultipartFormDataStreamProvider /// <summary> /// 重命名上传的文件 /// </summary> public class RenameMultipartFormDataStreamProvider : MultipartFormDataStreamProvider { #region 属性 /// <summary> /// 上传的文件名列表 /// </summary> public List<string> FileNameList { get; set; } = new List<string>(); #endregion public RenameMultipartFormDataStreamProvider(string root) : base(root) { } public override string GetLocalFileName(HttpContentHeaders headers) { //截取文件扩展名 string ext = Path.GetExtension(headers.ContentDisposition.FileName.TrimStart('"').TrimEnd('"')); string fileName = Guid.NewGuid().ToString().ToUpper() + ext; //base.GetLocalFileName(headers); if (!FileNameList.Contains(fileName)) { FileNameList.Add(fileName); } return fileName; } } #endregion #endregion } }
其中RenameMultipartFormDataStreamProvider是对上传文件进行重命名的类,继承自MultipartFormDataStreamProvider,MultipartFormDataStreamProvider会自动将上传的文件数据合并成FileStream,并会写入到重命名的所命名的文件中,为了便于调取文件名,特别增加了一个FileNameList属性来保存上传文件的原始文件名。
基本的数据包类/// <summary> /// 基本数据包 /// </summary> public class BaseDataPackage<T> { /// <summary> /// 状态(参见<seealso cref="StatusCode"/>) /// </summary> public int Status { get; set; } /// <summary> /// 信息 /// </summary> public string Message { get; set; } /// <summary> /// 具体数据 /// </summary> public T Data { get; set; } #region IsOK Function /// <summary> /// 是否OK /// </summary> /// <returns></returns> public bool IsOK() { return Status == 0;//StatusCode.OK; } #endregion }状态类
public class StatusCode { public const int OK = 0; public const int Fail = -1; }
附上用到的CSS样表
body { margin-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; background-image: url(/images/bg.jpg); } .all { height: 740px; 980px; margin: auto; } .head { height: 66px; 980px; float: left; } .body { height: 596px; 980px; float: left; } .body_style { height: 596px; 262px; float: left; background-image: url(images/body_02.jpg); } .footer { height: 78px; 980px; float: left; } .upload-preview-wrap { border: 1px solid; 100px; height: 120px; margin-top: 5px; margin-left: 5px; margin-right: 5px; margin-bottom: 5px; } .upload-preview-img{ 100px; height:120px; } .upload-wrap { position: relative; 158px; height: 43px; font-size: 16px; border: 1px solid #cacbcc; line-height: 43px; margin: 0 auto; color: #fff; text-align: center; background: #3385ff; border-bottom: 1px solid #2d78f4; float: left; } .upload-pic { position: absolute; font-size: 0; 100%; height: 100%; outline: 0; opacity: 0; filter: alpha(opacity=0); margin-left: -18px; z-index: 1; cursor: pointer; } .upload-container { height:180px; } .mask{ 100%; height: 100%; background: rgba(0, 0, 0, 0.8); position: fixed; top: 0; left: 0; z-index: 998; display: none; text-align:center; padding-top:10%; }
转载请注明出处