多部分正文长度限制异常

75
尽管在web.config部分将MaxRequestLengthmaxAllowedContentLength设为最大可能值,但ASP.Net Core仍不允许上传大于134,217,728字节的文件。来自Web服务器的确切错误如下:

处理请求时发生未处理的异常。

InvalidDataException: Multipart body length limit 134217728 exceeded.

有没有解决这个问题的方法?(ASP.Net Core

你能发布异常堆栈跟踪吗? - agua from mars
1
@aguafrommars:我已经解决了这个问题,请查看我的答案。 - Transcendent
4个回答

155

在 GitHub 上阅读了一些帖子后,我找到了解决此问题的方法。 结论是必须在 Startup 类中进行设置。 例如:

我在 GitHub 阅读了一些帖子之后找到了这个问题的解决方法。结论是它们必须在 Startup 类中进行设置。例如:

public void ConfigureServices(IServiceCollection services)
{
        services.AddMvc();
        services.Configure<FormOptions>(x => {
            x.ValueLengthLimit = int.MaxValue;
            x.MultipartBodyLengthLimit = int.MaxValue; // In case of multipart
        })
 }

这会解决问题。不过他们也提到了一个[RequestFormSizeLimit]属性,但我还没有找到相关的引用。


13
谢谢上帝,我找到了你,没有你我迷失了方向。 - daniherculano
2
还有人在最新版本中无法使其正常工作吗? - Jason Rowe
4
@JasonRowe 是的,我是。看起来它不能在最新版本中使用。 - Razort4x
2
OP没有提到Azure或IIS。 IIS / Azure确实会从web.config文件中读取一些设置,但Kestrel不会。 - Pawel
4
仍然出现错误(“超过了多部分正文长度限制1000000000”)。尝试将MultipartBodyLengthLimit设置为long.MaxValue,但由于某种原因被忽略。 - user3664254
显示剩余12条评论

30

或者使用属性,因此由Transcendant解析的操作的等效操作将是:

[RequestFormLimits(ValueLengthLimit = int.MaxValue, MultipartBodyLengthLimit = int.MaxValue)]

2

如果您像其他答案建议的那样使用int.MaxValue(2,147,483,647)作为MultipartBodyLengthLimit的值,您将允许上传大约2GB的文件,这可能会快速填满服务器的磁盘空间。我建议设置一个常量来限制文件上传到更合理的值,例如在Startup.cs中。

using MyNamespace.Constants;
public void ConfigureServices(IServiceCollection services)
{
        ... other stuff
        services.Configure<FormOptions>(options => {
            options.MultipartBodyLengthLimit = Files.MaxFileUploadSizeKiloBytes;
        })
 }

在一个单独的常量类中:

namespace MyNamespace.Constants
{
    public static class Files
    {
        public const int MaxFileUploadSizeKiloBytes = 250000000; // max length for body of any file uploaded
    }
}

1

如果仍然有人遇到这个问题,我创建了一个中间件来拦截请求并创建另一个主体。

    public class FileStreamUploadMiddleware
    {
        private readonly RequestDelegate _next;

        public FileStreamUploadMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            if (context.Request.ContentType != null)
            {
                if (context.Request.Headers.Any(x => x.Key == "Content-Disposition"))
                {
                    var v = ContentDispositionHeaderValue.Parse(
                        new StringSegment(context.Request.Headers.First(x => x.Key == "Content-Disposition").Value));
                    if (HasFileContentDisposition(v))
                    {
                        using (var memoryStream = new MemoryStream())
                        {
                            context.Request.Body.CopyTo(memoryStream);
                            var length = memoryStream.Length;
                            var formCollection = context.Request.Form =
                                new FormCollection(new Dictionary<string, StringValues>(),
                                    new FormFileCollection()
                                        {new FormFile(memoryStream, 0, length, v.Name.Value, v.FileName.Value)});
                        }
                    }
                }
            }

            await _next.Invoke(context);
        }

        private static bool HasFileContentDisposition(ContentDispositionHeaderValue contentDisposition)
        {
            // this part of code from  https://github.com/aspnet/Mvc/issues/7019#issuecomment-341626892
            return contentDisposition != null
                   && contentDisposition.DispositionType.Equals("form-data")
                   && (!string.IsNullOrEmpty(contentDisposition.FileName.Value)
                       || !string.IsNullOrEmpty(contentDisposition.FileNameStar.Value));
        }
    }

在控制器中,我们可以从请求中获取文件。
        [HttpPost("/api/file")]
        public IActionResult GetFile([FromServices] IHttpContextAccessor contextAccessor,
            [FromServices] IHostingEnvironment environment)
        {
            //save the file
            var files = Request.Form.Files;
            foreach (var file in files)
            {
                var memoryStream = new MemoryStream();
                file.CopyTo(memoryStream);

                var fileStream = File.Create(
                    $"{environment.WebRootPath}/images/background/{file.FileName}", (int) file.Length,
                    FileOptions.None);
                fileStream.Write(memoryStream.ToArray(), 0, (int) file.Length);

                fileStream.Flush();
                fileStream.Dispose();

                memoryStream.Flush();
                memoryStream.Dispose();
            }

            return Ok();
        }

你可以改进代码以满足你的需求,例如:在请求体中添加表单参数并对其进行反序列化。
我猜这是一个解决方法,但它能完成工作。

对于那些有所了解的人来说,对于IO操作,你需要使用异步IO...这是推荐的。你可以使用await file.CopyToAsync(fileStream)(不需要使用MemoryStream)。 - user2410689

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接