zoukankan      html  css  js  c++  java
  • NetCore-缓存文件上传和文件流上传

    NetCore官方给出的两种文件上传方式分别为“缓冲”、“流式”。

    1.缓冲处理:

    通过模型绑定先把整个文件保存到内存,然后我们通过IFormFile得到stream,优点是效率高,缺点对内存要求大。文件不宜过大。

    整个文件读入 IFormFile,它是文件的 C# 表示形式,用于处理或保存文件。 文件上传所用的资源(磁盘、内存)取决于并发文件上传的数量和大小。 如果应用尝试缓冲过多上传,站点就会在内存或磁盘空间不足时崩溃。 如果文件上传的大小或频率会消耗应用资源,请使用流式传输。

    2.流式处理:

    直接读取请求体装载后的Section 对应的stream 直接操作strem即可。无需把整个请求体读入内存

    从多部分请求收到文件,然后应用直接处理或保存它。 流式传输无法显著提高性能。 流式传输可降低上传文件时对内存或磁盘空间的需求。

    (1)创建FileStreamingHelper 帮助类

     相关StreamFile方法:

       public static async Task<MsgDto<string>> StreamFiles(this HttpRequest request, string targetDirectory)

            {

                if (!MultipartRequestHelper.IsMultipartContentType(request.ContentType))

                {

                    throw new Exception($"Expected a multipart request, but got {request.ContentType}");

                }

                var fileName = "";

                // Used to accumulate all the form url encoded key value pairs in the 

                // request.

                var formAccumulator = new KeyValueAccumulator();

                var boundary = MultipartRequestHelper.GetBoundary(

                    MediaTypeHeaderValue.Parse(request.ContentType),

                    _defaultFormOptions.MultipartBoundaryLengthLimit);

                var reader = new MultipartReader(boundary, request.Body);

                var filecanvsname = "";

                var itemindex = 1;

                var filesection = await reader.ReadNextSectionAsync();//用于读取Http请求中的第一个section数据

                var items = new List<string>();

                while (filesection != null)//遍历

                {

                    ContentDispositionHeaderValue contentDisposition;

                    var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(filesection.ContentDisposition, out contentDisposition);

                    /*

                       用于处理表单键值数据的section

                       -----------------------------99614912995

                       Content - Disposition: form - data; name = "SOMENAME"

                       Formulaire de Quota

                       -----------------------------99614912995

                       */

                    //字符格式

                    if (MultipartRequestHelper.HasFormDataContentDisposition(contentDisposition))

                    {

                        // Content-Disposition: form-data; name="key"

                        //

                        // value

                        // Do not limit the key name length here because the 

                        // multipart headers length limit is already in effect.

                        var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name);

                        var encoding = GetEncoding(filesection);

                        using (var streamReader = new StreamReader(

                            filesection.Body,

                            encoding,

                            detectEncodingFromByteOrderMarks: true,

                            bufferSize: 1024,

                            leaveOpen: true))

                        {

                            // The value length limit is enforced by MultipartBodyLengthLimit

                            var value = await streamReader.ReadToEndAsync();

                            if (String.Equals(value, "undefined", StringComparison.OrdinalIgnoreCase))

                            {

                                value = String.Empty;

                            }

                            formAccumulator.Append(key.Value, value); // For .NET Core <2.0 remove ".Value" from key

                            filecanvsname = value;

                            Log4netHelper.Write("StreamFiles:(Value)" + value);

                            if (formAccumulator.ValueCount > _defaultFormOptions.ValueCountLimit)

                            {

                                throw new InvalidDataException($"Form key count limit {_defaultFormOptions.ValueCountLimit} exceeded.");

                            }

                        }

                    }

                    //文件格式

                    else if (MultipartRequestHelper.HasFileContentDisposition(contentDisposition))

                    {

                           /*

                            用于处理上传文件类型的的section

                            -----------------------------99614912995

                            Content - Disposition: form - data; name = "files"; filename = "Misc 002.jpg"

                            ASAADSDSDJXCKDSDSDSHAUSAUASAASSDSDFDSFJHSIHFSDUIASUI+/==

                            -----------------------------99614912995

                        */

                        if (!Directory.Exists(targetDirectory))

                        {

                            Directory.CreateDirectory(targetDirectory);

                        }

                        Log4netHelper.Write("StreamFiles:(Size)" + filesection.Body.Length);

                        fileName = MultipartRequestHelper.GetFileName(contentDisposition);

                        //获得文件扩展名

                        string fileNameEx = Path.GetExtension(fileName);

                        if (!string.IsNullOrEmpty(fileNameEx)) fileNameEx = fileNameEx.ToLower();

                        if (!new List<string> { ".xls", ".xlsx", ".doc", ".docx", ".pdf", ".png", ".jpg", ".jpeg", ".zip", ".rar" }.Contains(fileNameEx))

                        {

                            return new MsgDto<string> { msg = "附件文件格式有误" };

                        }

                        var time = DateTime.Now.ToString("yyyyMMddHHmmssfff");

                        fileName = time + "_" + itemindex + fileNameEx;

                        Log4netHelper.Write("StreamFiles:(fileNameEx)" + fileName);

                        var loadBufferBytes = 1024;//这个是每一次从Http请求的section中读出文件数据的大小,单位是Byte即字节,这里设置为1024的意思是,每次从Http请求的section数据流中读取出1024字节的数据到服务器内存中,然后写入下面targetFileStream的文件流中,可以根据服务器的内存大小调整这个值。这样就避免了一次加载所有上传文件的数据到服务器内存中,导致服务器崩溃。

                        using (var targetFileStream = System.IO.File.Create(targetDirectory + "\" + fileName))

                        {

                            //section.Body是System.IO.Stream类型,表示的是Http请求中一个section的数据流,从该数据流中可以读出每一个section的全部数据,所以我们下面也可以不用section.Body.CopyToAsync方法,而是在一个循环中用section.Body.Read方法自己读出数据,再将数据写入到targetFileStream

                            await filesection.Body.CopyToAsync(targetFileStream, loadBufferBytes);

                        }

                        items.Add(fileName);

                        itemindex++;

                    }

                    filesection = await reader.ReadNextSectionAsync();//用于读取Http请求中的下一个section数据

                }

                var rebackurl = "";

                if (items.Any())

                {

                    var topname = items.First().Split('_')?.First() ?? "";

                    if (items.Count == 1)

                    {

                        foreach (var fileitem in items)

                        {

                            var fileNameEx = fileitem.Split('.').Last();

                            if (File.Exists(targetDirectory + "\" + fileitem))

                            {

                                File.Move(targetDirectory + "\" + fileitem, targetDirectory + "\" + filecanvsname + topname + "." + fileNameEx);

                                rebackurl = filecanvsname + topname + "." + fileNameEx;

                            }

                        }

                    }

                    else

                    {

                        //多个文件合并压缩包    

                        var fileinfos = new List<FileInfo>();

                        foreach (var fileitem in items)

                        {

                            if (!string.IsNullOrEmpty(fileitem))

                            {

                                var filename = targetDirectory + "\" + fileitem;

                                fileinfos.Add(new FileInfo(filename));

                            }

                        }

                        rebackurl = ZipHelper.ZipCompress(fileinfos, targetDirectory, filecanvsname + topname);

                    }

                }

                return new MsgDto<string> { success = true, data = rebackurl, msg = "上传成功" };

            }

    (2)相关调用方法:

    创建DisableFormValueModelBindingAttribute属性方法:

    用于上传大型文件,以防止表单数据被读入内存、防止模型绑定访问表单数据

    (3)文件限制大小:

    Startup相关ConfigureServices加上方法

     //设置接收文件长度的最大值。

    services.Configure<FormOptions>(x =>

    {

          x.ValueLengthLimit = int.MaxValue;

          x.MultipartBodyLengthLimit = int.MaxValue;

          x.MultipartHeadersLengthLimit = int.MaxValue;

    });

    (4)iis限制文件大小

    发布后创建web.config设置允许最大传输的文件大小

     webapi接口调用方法:

  • 相关阅读:
    JSON.parse解决Unexpected token ' in JSON at position 1报错
    angularjs $scope与this的区别,controller as vm有何含义?
    angularjs link compile与controller的区别详解,了解angular生命周期
    理解Promise.all,Promise.all与Promise.race的区别,如何让Promise.all在rejected失败后依然返回resolved成功结果
    angularjs 一篇文章看懂自定义指令directive
    js 记录几个因惯性思维引发的代码BUG,开发思维方式的自我反省
    js forEach参数详解,forEach与for循环区别,forEach中如何删除数组元素
    angularjs ng-if妙用,ng-if解决父子组件异步传值
    JS 从内存空间谈到垃圾回收机制
    Markdown数学公式语法
  • 原文地址:https://www.cnblogs.com/wulala9/p/15319888.html
Copyright © 2011-2022 走看看