Contents

.Net Core Controller 下載檔案/上傳檔案

提到網頁功能一定會有下載檔案/上傳檔案需求,這邊整理會用到方法與遇到上傳檔案問題。

下載檔案

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
        public IActionResult Get([FromQuery] GetFileDownloadRequest request)
        {
            FileTypeEnum fileType = request.ReqFileType;
            string filePath = "";
            ... getFilePath

            if (string.IsNullOrEmpty(filePath))
            {
                return NotFound();
            }
            string fileName = Path.GetFileName(filePath);
            return PhysicalFile(filePath, "application/octet-stream",fileDownloadName: fileName);
        }

下載依照 Content-Type 設定metadata

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[HttpGet("[action]")]
public async Task<IActionResult> GetFile(string id)
{
    var dir = "GetOrBuildYourDirectoryString";
    var fileName = "GetYourFileName";
 
    var mimeType = GetMimeType(fileName);
 
    var path = Path.Combine(dir, fileName);
 
    return PhysicalFile(path, mimeType, version.FileName);
}
 
public string GetMimeType(string fileName)
{
    var provider = new FileExtensionContentTypeProvider();
    string contentType;
    if (!provider.TryGetContentType(fileName, out contentType))
    {
        contentType = "application/octet-stream";
    }
 
    return contentType;
}

參考:在TypeScript和ASP.NET Core中处理文件上传和受保护的下载_寒冰屋的博客-CSDN博客

可接續下載方法

1
2
3
4
5
6
7
8
9
[HttpGet("[action]")]
public async Task<IActionResult> GetFile(string id)
{
            string filePath = "xxx";
            string fileName = Path.GetFileName(filePath);
            new FileExtensionContentTypeProvider().TryGetContentType(fileName, out string contentType);
            contentType ??= "application/octet-stream";
            return PhysicalFile(filePath, contentType, fileDownloadName: fileName, enableRangeProcessing: true);
}

主要是 enableRangeProcessing 設定為 true 就能做到,想當初 PHP 實作這個方法看到吐。

相關別的程式作法:

參考

上傳檔案

推薦看下官方文件,裡面有滿多東西
推薦看下官方文件,裡面有滿多東西
推薦看下官方文件,裡面有滿多東西
在 ASP.NET Core 中上傳檔案 | Microsoft Docs

這邊先紀錄單純上傳方法,可能沒官方有做一些嚴謹判斷,有空再來開一篇記錄。(有空的話😜)

 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
38
39
40
41
42
43
44
45
46
47
        // 自行注入
        private readonly IConfiguration _configuration;
        private readonly IWebHostEnvironment _env;


        [HttpPost]
        [Route("[controller]")]
        public IActionResult Post([FromForm]Request request){
            ...
            UploadFile(...,request.UploadFile);
            ...
        }

        private void UploadFile(string paramPathName, IFormFile uploadFile)
        {
            string filepath = ProcessUploadedFile(paramPathName, "paramFileTypePath", uploadFile);

            ...
        }

        private string ProcessUploadedFile(string paramPathName, string directory,IFormFile formFile)
        {
            string uniqueFileName = null;
            if (string.IsNullOrWhiteSpace(_env.WebRootPath))
            {
                _env.WebRootPath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
            }
            string datePath = DateTime.Now.ToString("yyyyMMdd");
            string rootPath = _configuration["UploadFilePath"] ?? _env.WebRootPath;
            string uploadsFolder = Path.Combine(rootPath, "upload", paramPathName, directory, datePath);


            if (formFile != null)
            {

                Directory.CreateDirectory(uploadsFolder);
                uniqueFileName = Guid.NewGuid().ToString() + "_" + formFile.FileName;
                string filePath = Path.Combine(uploadsFolder, uniqueFileName);


                using var fileStream = new FileStream(filePath, FileMode.Create);
                formFile.CopyTo(fileStream);
            }
            return Path.Combine(uploadsFolder, uniqueFileName);
        }

    }

上傳限制調整

主要參考這兩個網站整理資訊。

設定 IIS 上傳限制

IIS 的 web.config 設定

1
2
3
4
5
6
7
8
 <!--//上传文件大小限制IIS设置 256M -->
  <system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="268435456" />
    </requestFiltering>
  </security>
</system.webServer>

這段程式碼的關鍵:requestLimits maxAllowedContentLength=“209715200” 。這句話的意思就是把允許上傳的最大檔案設定為256M。這個值,你們可以根據自己項目的需要實際設定。這裡,我們先暫時設定成256M。

這邊要我們用算的可能很不方便,我這邊有找到這個網站可以算出。

奇怪怎麼沒有找到前端版本

  1. 這邊 Kestrel 也要設定上限(MaxRequestBodySize)
    預設的要求本文大小上限為 30,000,000 個位元組,大約是 28.6 MB。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
     //上傳文件大小限制Kestrel設置
                services.Configure<KestrelServerOptions>(options =>
                {
                    // Set the limit to 256 MB
                    options.Limits.MaxRequestBodySize = 268435456;
                });
    //上傳文件大小限制IIS設置(下面這段可以不用複製)
                services.Configure<IISServerOptions>(options =>
                {
                    options.MaxRequestBodySize = long.Parse(Configuration.GetSection("Kestrel").Value);
                });

但是沒設定的話,會出現Request body too large 錯誤

IISServerOptions我看這段很奇怪,使用上去沒有成功。後來看到:

獲取或設置 HttpRequest 的最大請求正文大小。請注意,IIS 本身俱有限制 maxAllowedContentLength,它將在 IISServerOptions 中設置的 MaxRequestBodySize 之前處理。更改 MaxRequestBodySize 不會影響 maxAllowedContentLength。要增加 maxAllowedContentLength,請在 web.config 中添加一個條目以將 maxAllowedContentLength 設置為更高的值。有關詳細信息,請參閱配置。

https://i.imgur.com/5mT6OXi.png

參考: AspNetCore.Docs/in-process-hosting.md at main · dotnet/AspNetCore.Docs

Request body too large 錯誤

Post的body大概超過30m就會報這個錯誤,完整的錯誤是Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Request body too large
解決的方法有二種,一種是在請求的函數前加Attribute

1
2
3
4
5
[HttpPost("upload")]
[RequestSizeLimit(100_000_000)] //最大100m左右
//[DisableRequestSizeLimit]  //或者取消大小的限制
public async Task<string> UploadFiles(ModelTest test)
{

還有一種方法是全域增加請求body的大小或者不限body的大小。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public static IWebHost BuildWebHost(string[] args)
{
    var config = new ConfigurationBuilder()
    .AddCommandLine(args)
    .Build();
    return WebHost.CreateDefaultBuilder(args).UseConfiguration(config)
        .UseStartup<Startup>().UseKestrel(options =>
        {
          //所有controller都不限制post的body大小
            options.Limits.MaxRequestBodySize = null;
        })
        .Build();
}

Multipart body length limit 134217728 exceeded錯誤

Post的body大概超過100多M會碰到這個錯誤,完整的錯誤資訊是 System.IO.InvalidDataException: Multipart body length limit 134217728 exceeded.

整體設定方法

解決的方法是在 startup.cs 新增程式碼:

1
2
3
4
5
6
7
8
9
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    //解決Multipart body length limit 134217728 exceeded
    services.Configure<FormOptions>(x =>
    {
        x.ValueLengthLimit = int.MaxValue;
        x.MultipartBodyLengthLimit = int.MaxValue; // In case of multipart
    });

RequestFormLimitsAttribute 用來設定 MultipartBodyLengthLimit 單一頁面或動作的 。

單一頁面設定放法

這段程式碼的關鍵:requestLimits maxAllowedContentLength=“209715200” 。這句話的意思就是把允許上傳的最大檔案設定為200M。這個值,你們可以根據自己項目的需要實際設定。這裡,我們先暫時設定成200M。這時,我們再跑一下項目,會發現404錯誤已經不見了,取而代之的是另一種錯誤:

這段錯誤程式碼的意思是表單上傳的檔案長度超過了134217727KB,要解決這個問題,我們只有在Home控製器裡的Upload方法前加入如下屬性聲明:[RequestFormLimits(MultipartBodyLengthLimit = 209715200)]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[HttpPost]
[RequestFormLimits(MultipartBodyLengthLimit = 209715200)]
public IActionResult Upload(IFormFile file,
[FromServices] IHostingEnvironment env)
{
  ...
  ...
}

 

基本上使用 IIS 和 Kestrel 都需要設定的。

Nginx 限制上傳大小的錯誤

通過Nginx連接埠對應,請求會先經過Nginx,也需要額外再設定一個參數client_max_body_size,否則上傳大檔案也會有問題

1
2
3
4
5
location ^~ /sample2/ {
    rewrite  ^/sample2/(.*)$ /$1 break;          
    proxy_pass http://localhost:5556;
    client_max_body_size    300m; #最大接受300m檔案以內的
}

Kestrel 中的解決方案

如果你的程序時運行在 Kestrel 上,你可以在應用程式等級(整個系統),或者是 Action 等級控制上傳檔案的大小。

你如果要在 Action 等級控制上傳檔案的大小,你需要設定兩個屬性[RequestSizeLimit] 和 [RequestFormLimits].

[RequestSizeLimit]屬性設定請求的最大長度(以位元組為單位),而[RequestFormLimits]設定多部分正文長度的最大長度。

1
2
3
4
5
6
7
8
9
[HttpPost]
[RequestFormLimits(MultipartBodyLengthLimit = 209715200)]
[RequestSizeLimit(209715200)]
public IActionResult Upload(IFormFile file,
[FromServices] IHostingEnvironment env)
{
  ...
  ...
}

想要全域設定

1
2
3
4
5
6
7
8
public static IWebHostBuilder CreateWebHostBuilder
(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel(options =>
        {
            options.Limits.MaxRequestBodySize = 209715200;
        });

也需要設定

1
2
3
4
5
6
7
8
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.Configure<FormOptions>(x =>
    {
        x.MultipartBodyLengthLimit = 209715200;
    });
}

設定總結

光一個上傳檔案有這麼多選項,這邊做個整理,剛開始看也不知道差在哪邊。

用 IIS 通常會要設定 XML requestLimits maxAllowedContentLength,相關 MultipartBodyLengthLimit也要在程式設定。

用 Kestrel 一樣也要設定這些東西(MaxRequestBodySizeMaxRequestBodySize),兩個都家都不會有影響。

其他相關文件(整理前參考)

Serving video file / stream from ASP Net Core 6 Minimal API - Microsoft Q&A

net core api上传下载大文件 413、400错误 IIS服务器 - wskxy - 博客园

1
2
        [RequestFormLimits(MultipartBodyLengthLimit = 209715200)]
        [HttpPost("Up/{id}")]

1
2
3
4
            services.Configure<IISServerOptions>(options =>
            {
                options.MaxRequestBodySize = 209715200;
            });

參考:.net Core 解决Form value count limit 1024 exceeded. (文件上传过大) - Pursue - 博客园

怎流一個上傳檔案容量限制就有這麼多種設定方法。

彩蛋

ASP.NET Core 一行程式碼搞定檔案上傳-知識星球

環境變數