• 循序渐进学.Net Core Web Api开发系列【5】:文件上传


    系列目录

    循序渐进学.Net Core Web Api开发系列目录

     本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi

    一、概述

    本篇介绍通过.NET core WebApi实现文件上传。

    二、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;
                ...
           }

    除了获取到files的方式不同,其他操作完全一致。

    三、前端代码

    <!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")); 比较方便。

    四、上传大文件

    上传大文件和上传小文件代码是一样的,关键是上传大文件时间太长,需要前端提供进度报告,不然用户体验太差。

    解决的方案是利用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 {
                 550px;
                height: 10px;
                border: 2px solid #09F;
            }
            #son {
                 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>

     此时,可以看到上传进度条了,可以选择一个大文件测试一下。或者把浏览器的传输速度调低也可以进行调试。

    五、为Swagger提供文件上传调试功能支持

    目前swagger是不支持List<IFormFile>类型的,为了方便调试,我们为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选择文件了

    但有个缺点是只能上传一个文件,如何实现可以选择多个文件还不会搞。

  • 相关阅读:
    OneFlow: 从 Op 到 Job
    琐碎知识点
    JS中的运算符优先级
    JS中的小括号,中括号,大括号
    逻辑与和逻辑或的短路运算
    类型转换
    一些好用的插件推荐
    src漏洞挖掘思路参考(待完善)
    计算机中的攻与防之效率提高篇(更新中)
    简单制作便携版浏览器
  • 原文地址:https://www.cnblogs.com/seabluescn/p/9229760.html
Copyright © 2020-2023  润新知