• 【.Net Core】上传文件-IFormFile


    安全注意事项

    向用户提供向服务器上传文件的功能时,必须格外小心。 攻击者可能会尝试执行以下操作:

    • 执行拒绝服务攻击。
    • 上传病毒或恶意软件。
    • 以其他方式破坏网络和服务器。

    降低成功攻击可能性的安全措施如下:

    • 将文件上传到专用文件上传区域,最好是非系统驱动器。 使用专用位置便于对上传的文件实施安全限制。 禁用对文件上传位置的执行权限。
    • 请勿将上传的文件保存在与应用相同的目录树中。
    • 使用应用确定的安全的文件名。 请勿使用用户提供的文件名或上传的文件的不受信任的文件名。 当显示不受信任的文件名时 HTML
      会对它进行编码。 例如,记录文件名或在 UI 中显示(Razor 自动对输出进行 HTML 编码)。
    • 按照应用的设计规范,仅允许已批准的文件扩展名。
    • 验证是否对服务器执行客户端检查。客户端检查易于规避。
    • 检查已上传文件的大小。 设置一个大小上限以防止上传大型文件。
    • 文件不应该被具有相同名称的上传文件覆盖时,先在数据库或物理存储上检查文件名,然后再上传文件。
    • 先对上传的内容运行病毒/恶意软件扫描程序,然后再存储文件。

    存储方案

    常见的文件存储选项有:

    • 数据库
      • 对于小型文件上传,数据库通常快于物理存储(文件系统或网络共享)选项。
      • 相对于物理存储选项,数据库通常更为便利,因为检索数据库记录来获取用户数据可同时提供文件内容(如头像图像)。
      • 相较于使用云数据存储服务,数据库的成本可能更低。
    • 物理存储(文件系统或网络共享)
      • 对于大型文件上传:
        • 数据库限制可能会限制上传的大小。
        • 相对于数据库存储,物理存储通常成本更高。
      • 相较于使用云数据存储服务,物理存储的成本可能更低。
      • 应用的进程必须具有存储位置的读写权限。 切勿授予执行权限。
    • 云数据存储服务(例如 Azure Blob 存储)。
      • 服务通常通过本地解决方案提供提升的可伸缩性和复原能力,而它们往往受单一故障点的影响。
      • 在大型存储基础结构方案中,服务的成本可能更低。

    文件上传方案

    文件上传方案

    缓冲和流式传输是上传文件的两种常见方法。

    缓冲

    整个文件将读入一个 IFormFile。 IFormFile 是用于处理或保存文件的 C# 表示形式。

    文件上传使用的磁盘和内存取决于并发文件上传的数量和大小。 如果应用尝试缓冲过多上传,站点就会在内存或磁盘空间不足时崩溃。 如果文件上传的大小或频率会消耗应用资源,请使用流式传输。

    会将大于 64 KB 的所有单个缓冲文件从内存移到磁盘的临时文件。

    用于较大请求的 ASPNETCORE_TEMP 临时文件将写入环境变量中命名的位置。 如果未 ASPNETCORE_TEMP 定义,文件将写入当前用户的临时文件夹。

    本主题的以下部分介绍了如何缓冲小型文件:

    • 物理存储
    • Database

    流式处理

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

    .NET Core Web APi FormData多文件上传,IFormFile强类型文件灵活绑定

    前端使用FormData进行实现批量上传

    <!doctype html>
    
    <html>
    <head>
        <meta charset="utf-8">
    
        <title>上传</title>
    
    </head>
    <form method="post" id="uploadForm" enctype="multipart/form-data">
        <input type="file" name="file" multiple />
        <input type="button" value="上传" onclick="doUpload()" />
    </form>
    <body>
        <script src="https://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
        <script>
            $(function () {
            });
            function doUpload() {
                var formData = new FormData($("#uploadForm")[0]);
                $.ajax({
                    url: 'http://localhost:5000/api/Path/Upload',
                    type: 'post',
                    data: formData,
                    async: false,
                    cache: false,
                    contentType: false,
                    processData: false,
                    success: function (returndata) {
                        console.dir(returndata);
                    },
                    error: function (returndata) {
                        console.dir(returndata);
                    }
                })
            }
        </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    批量上传选择多个文件:
    在这里插入图片描述后端.Net Core 使用 IFormFile 强类型灵活绑定获取文件信息

    /// 
            /// 文件上传
            /// 
            /// 
            [HttpPost]
            public MethodResult Upload([FromForm(Name = "file")] List<IFormFile> files)
            {
                files.ForEach(file =>
                {
                    var fileName = file.FileName;
                    string fileExtension = file.FileName.Substring(file.FileName.LastIndexOf(".") + 1);//获取文件名称后缀 
                    //保存文件
                    var stream = file.OpenReadStream();
                    // 把 Stream 转换成 byte[] 
                    byte[] bytes = new byte[stream.Length];
                    stream.Read(bytes, 0, bytes.Length);
                    // 设置当前流的位置为流的开始 
                    stream.Seek(0, SeekOrigin.Begin);
                    // 把 byte[] 写入文件 
                    FileStream fs = new FileStream("D:\" + file.FileName, FileMode.Create);
                    BinaryWriter bw = new BinaryWriter(fs);
                    bw.Write(bytes);
                    bw.Close();
                    fs.Close();
                });
                return new MethodResult("success", 1);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    上传后文件:
    在这里插入图片描述

    上传转发

    
    
            [HttpPost, Route("UploadFile")]
            public JsonActionResult<bool> UploadFileCallDifferentServerWebApi()
            {
                JsonActionResult<bool> jsonActionResult = new JsonActionResult<bool>();
                var client = new RestClient("https://localhost:44302/Maint/SysFile/Upload"); client.Timeout = -1;
                var request = new RestRequest(Method.POST);
                request.AddHeader("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJDLL_Gc");
                request.AddHeader("Content-Type", "multipart/form-data");
                var httpRequest = System.Web.HttpContext.Current.Request;
                foreach (string key in httpRequest.Files)  // 文件键
                {
                    var postedFile = httpRequest.Files[key];    // 获取文件键对应的文件对象 
                    Byte[] fileData = new Byte[postedFile.ContentLength];
                    Stream sr = postedFile.InputStream;//创建数据流对象 
                    sr.Read(fileData, 0, postedFile.ContentLength);
                    request.AddFile("fileInput", fileData, key);
                }
                request.AddParameter("pageCode", "jlysFile");
                var response = client.Execute(request);
                //string str = string.Empty;
                //var httpRequest = System.Web.HttpContext.Current.Request;   
                //foreach (string key in httpRequest.Files)  // 文件键
                //{
                //    var postedFile = httpRequest.Files[key];    // 获取文件键对应的文件对象 
                //    Byte[] fileData = new Byte[postedFile.ContentLength];
                //    Stream sr = postedFile.InputStream;//创建数据流对象 
                //    sr.Read(fileData, 0, postedFile.ContentLength);
                //    str = System.Text.Encoding.UTF8.GetString(fileData);
    
                //} 
                //result = HttpManager.HttpPostCarryDefaultToken("https://localhost:44302/Maint/SysFile/Upload", str, "multipart/form-data");
                return jsonActionResult;
    
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    验证

    内容验证

    将第三方病毒/恶意软件扫描 API 用于上传的内容。

    在大容量方案中,在服务器资源上扫描文件较为困难。 若文件扫描导致请求处理性能降低,请考虑将扫描工作卸载到后台服务,该服务可以是在应用服务器之外的服务器上运行的服务。 通常会将卸载的文件保留在隔离区,直至后台病毒扫描程序检查它们。 文件通过检查时,会将相应的文件移到常规的文件存储位置。 通常在执行这些步骤的同时,会提供指示文件扫描状态的数据库记录。 通过此方法,应用和应用服务器可以持续以响应请求为重点。

    文件扩展名验证

    应在允许的扩展名列表中查找上传的文件的扩展名。 例如:

    private string[] permittedExtensions = { ".txt", ".pdf" };
    
    var ext = Path.GetExtension(uploadedFileName).ToLowerInvariant();
    
    if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext))
    {
        // The extension is invalid ... discontinue processing the file
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    文件签名验证

    文件的签名由文件开头部分中的前几个字节确定。 可以使用这些字节指示扩展名是否与文件内容匹配。 示例应用检查一些常见文件类型的文件签名。 在下面的示例中,在文件上检查 JPEG 图像的文件签名:

    private static readonly Dictionary<string, List<byte[]>> _fileSignature = 
        new Dictionary<string, List<byte[]>>
    {
        { ".jpeg", new List<byte[]>
            {
                new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 },
                new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 },
                new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 },
            }
        },
    };
    
    using (var reader = new BinaryReader(uploadedFileData))
    {
        var signatures = _fileSignature[ext];
        var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length));
    
        return signatures.Any(signature => 
            headerBytes.Take(signature.Length).SequenceEqual(signature));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    文件名安全

    切勿使用客户端提供的文件名来将文件保存到物理存储。 使用 Path.GetRandomFileName 或 Path.GetTempFileName 为文件创建安全的文件名,以创建完整路径(包括文件名)来执行临时存储。

    Razor 自动对属性值执行 HTML 编码以进行显示。 以下代码安全可用:

    @foreach (var file in Model.DatabaseFiles) {
        <tr>
            <td>
                @file.UntrustedName
            </td>
        </tr>
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    大小验证

    限制上传的文件的大小。

    在示例应用中,文件大小限制为 2 MB(以字节为单位)。 通过 appsettings.json 文件中的配置来提供此限制:

    {
      "FileSizeLimit": 2097152
    }
    
    • 1
    • 2
    • 3

    将 FileSizeLimit 注入到 PageModel 类:

    public class BufferedSingleFileUploadPhysicalModel : PageModel
    {
        private readonly long _fileSizeLimit;
    
        public BufferedSingleFileUploadPhysicalModel(IConfiguration config)
        {
            _fileSizeLimit = config.GetValue<long>("FileSizeLimit");
        }
    
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    文件大小超出限制时,将拒绝文件:

    if (formFile.Length > _fileSizeLimit)
    {
        // The file is too large ... discontinue processing the file
    }
    
    • 1
    • 2
    • 3
    • 4

    使名称属性值与 POST 方法的参数名称匹配

    在发布窗体数据或直接使用 JavaScript 的 FormData 的非 Razor 窗体中,窗体元素或 FormData 中指定的名称必须与控制器操作中的参数名称相匹配。

    在以下示例中

    使用 元素时,将 name 属性设置为值 battlePlans:

    <input type="file" name="battlePlans" multiple>
    
    • 1

    使用 JavaScript FormData 时,将名称设置为值 battlePlans:

    var formData = new FormData();
    
    for (var file in files) {
      formData.append("battlePlans", file, file.name);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    将匹配的名称用于 C# 方法的参数 (battlePlans)
    对于名为 Upload 的 Razor Pages 页面处理程序方法:

    public async Task<IActionResult> OnPostUploadAsync(List<IFormFile> battlePlans)
    
    • 1

    对于 MVC POST 控制器操作方法:

    public async Task<IActionResult> Post(List<IFormFile> battlePlans)
    
    • 1

    来源

    .NET Core Web APi FormData多文件上传,IFormFile强类型文件灵活绑定
    在 ASP.NET Core 中上传文件

  • 相关阅读:
    园区网典型配置案例
    (Ljava/lang/String;)Ljava/lang/Integer; @65: areturn
    MachineLearning 12. 机器学习之降维方法t-SNE及可视化 (Rtsne)
    基于 TLS 1.3的百度安全通信协议 bdtls 介绍
    【漏洞复现】泛微e-office OfficeServer2.php 存在任意文件读取漏洞复现
    唤醒手腕 Matlab 游戏编程常用技术知识点详细教程(更新中)
    【Android面试八股文】如何实现Activity窗口快速变暗
    提供CY系列菁染料CY3、CY5、CY5.5、CY7、CY7.5,ICG,荧光素FITC,Bodipy系列染料标记葡聚糖Dextran
    康力健身中心
    十三、函数式编程(1)
  • 原文地址:https://blog.csdn.net/weixin_44231544/article/details/127999723