文件上传
1 本篇概述
本篇介绍通过.NET core WebApi实现文件上传。
2 Controller端代码
直接上代码:
[Route("api/files")]
[Produces("application/json")]
public class FileController: Controller
{
private readonly IHostingEnvironment _hostingEnvironment;
public FileController(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
[HttpPost("iform")]
public ResultObject UploadIForm(List < IFormFile > files)
{
List < String > filenames = new List < string > ();
foreach(var file in files)
{
var fileName = file.FileName;
Console.WriteLine(fileName);
fileName = $ "/UploadFile/{fileName}";
filenames.Add(fileName);
fileName = _hostingEnvironment.WebRootPath + fileName;
using(FileStream fs = System.IO.File.Create(fileName))
{
file.CopyTo(fs);
fs.Flush();
}
}
return new ResultObject
{
state = "Success",
resultObject = filenames
};
}
为了同时返回上传的文件名和操作状态,我们定义了一个ResultObject的类来返回信息。
public class ResultObject
{
public String state
{
get;
set;
}
public Object resultObject
{
get;
set;
}
}
返回上传的文件名称列表的目的是因为后台可能会给文件重新命名,以避免冲突,新的文件名称需要让前台知道。
后台的files是通过传入的参数获得的,还有其他两种方法可以获取到文件,效果一样:
[HttpPost]
public ResultObject UploadAjax()
{
var files = Request.Form.Files;...
}
或:
public ResultObject UploadForm(IFormCollection form)
{
var files = form.Files;...
}
3 前端代码
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <script src="lib/jquery/dist/jquery.min.js"></script> <script src="lib/jquery-form/dist/jquery.form.min.js"></script> <script type="text/javascript"> function AjaxFormDataUploadfile() { var fileUpload = $("#files").get(0); var files = fileUpload.files; var data = new FormData(); for (var i = 0; i < files.length; i++) { data.append("files", files[i]); } $.ajax({ type: "POST", url: "/api/files/iform", contentType: false, processData: false, data: data, success: function (message) { alert("success"); }, error: function () { alert("上传文件出现错误!"); } }); } </script> </head> <body> <form id="myform" method="post" action="/api/files/iform" enctype="multipart/form-data"> <input type="file" id="files" name="files" multiple /> <br /><br /> <input id="formupload" type="button" value="Form Data Upload" onclick="AjaxFormDataUploadfile();" /><br /><br /> </form> </body> </html>
两个注意点:
- form要提供enctype="multipart/form-data" 属性
- 如果要上传多个文件,文件表单需要提供multiple属性
这里我们通过FormData对象的append操作来构建提交的数据,根据我上一篇博客提到的,这里提交数据还有其他两个办法:
function formuploadfile() { $("#myform").ajaxSubmit(); }
或者
function Ajaxuploadfile() { var formdata = new FormData(document.getElementById("myform")); $.ajax({ type: "POST", url: "/api/files/iform", contentType: false, processData: false, data: formdata, success: function(result) { alert("success"); $.each(result.resultObject, function(i, filename) { alert(filename); }); }, error: function() { alert("上传文件出现错误!"); } }); }
具体选择哪种方式,要看前端的需求,个人认为 var formdata = new FormData(document.getElementById("myform")); 比较方便。
4 上传大文件
上传大文件和上传小文件代码是一样的,关键是上传大文件时间太长,需要前端提供进度报告,不然用户体验太差。
解决的方案是利用ajax的HXR对象。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <script src="lib/jquery/dist/jquery.min.js"></script> <script src="lib/jquery-form/dist/jquery.form.min.js"></script> <script type="text/javascript"> function AjaxuploadfileWithprogress() { var formdata = new FormData(document.getElementById("myform")); $.ajax({ type: "POST", url: "/api/files/iform", contentType: false, processData: false, data: formdata, success: function (result) { alert("success"); $.each(result.resultObject, function (i, filename) { alert(filename); }); }, error: function () { alert("上传文件出现错误!"); }, xhr: function () { var xhr = $.ajaxSettings.xhr(); if (onprogress && xhr.upload) { xhr.upload.addEventListener("progress", onprogress, false); return xhr; } } }); } function onprogress(evt) { var loaded = evt.loaded; //已经上传大小 var tot = evt.total; //附件总大小 var per = Math.floor(100 * loaded / tot); //百分比 $("#son").html(per + "%"); $("#son").css("width", per + "%"); } </script> <style type="text/css"> #parent { width: 550px; height: 10px; border: 2px solid #09F; } #son { width: 0; height: 100%; background-color: #09F; text-align: center; line-height: 10px; font-size: 20px; font-weight: bold; } </style> </head> <body> <form id="myform" method="post" action="/api/files/iform" enctype="multipart/form-data"> <div> <div> <br /> <p>表单多个上传文件:</p><br /> <input type="file" id="files" name="files" multiple /> <br /><br /> <input id="formuploadprogress" type="button" value="Ajax Upload Progress" onclick="AjaxuploadfileWithprogress();" /><br /><br /> <div id="parent"> <div id="son"></div> </div> </div> </div> </form> </body> </html>
此时,可以看到上传进度条了,可以选择一个大文件测试一下。或者把浏览器的传输速度调低也可以进行调试。

5 为Swagger提供文件上传调试功能支持
目前swagger是不支持List
新增类:
public class SwaggerFileUploadListFilter: IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
if(!context.ApiDescription.HttpMethod.Equals("POST", StringComparison.OrdinalIgnoreCase) && !context.ApiDescription.HttpMethod.Equals("PUT", StringComparison.OrdinalIgnoreCase))
{
return;
}
var fileParameters = context.ApiDescription.ActionDescriptor.Parameters.Where(n => n.ParameterType == typeof(List < IFormFile > )).ToList();
if(fileParameters.Count < 0)
{
return;
}
operation.Consumes.Add("multipart/form-data");
foreach(var fileParameter in fileParameters)
{
var parameter = operation.Parameters.Single(n => n.Name == fileParameter.Name);
operation.Parameters.Remove(parameter);
NonBodyParameter p = new NonBodyParameter
{
Name = parameter.Name,
In = "formData",
Description = parameter.Description,
Required = parameter.Required,
Type = "file"
};
operation.Parameters.Add(p);
}
}
}
在Startup类的ConfigureServices方法里注册该过滤器:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddCors();
services.AddSwaggerGen(option =>
{
option.SwaggerDoc("v1", new Info
{
Version = "v1",
Title = "SaleService接口文档",
Description = "RESTful API for SaleService.",
TermsOfService = "None",
Contact = new Contact
{
Name = "seabluescn", Email = "seabluescn@163.com", Url = ""
}
});
option.OperationFilter < SwaggerFileUploadListFilter > ();
//Set the comments path for the swagger json and ui.
var basePath = PlatformServices.Default.Application.ApplicationBasePath;
var xmlPath = Path.Combine(basePath, "SaleService.xml");
option.IncludeXmlComments(xmlPath);
});
}
此时可以通过swagger选择文件了

但有个缺点是只能上传一个文件,如何实现可以选择多个文件还不会搞。
版权声明:
本文为智客工坊「seabluescn」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
