文件上传

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>

  两个注意点:

  1. form要提供enctype="multipart/form-data" 属性
  2. 如果要上传多个文件,文件表单需要提供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类型的,为了方便调试,我们为swagger提供该类型的支撑。

  新增类:

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版权协议,转载请附上原文出处链接及本声明。

results matching ""

    No results matching ""