为啥我在尝试将大文件发布到核心 Web API 时出现 404

Posted

技术标签:

【中文标题】为啥我在尝试将大文件发布到核心 Web API 时出现 404【英文标题】:Why do I get a 404 trying to post a large file to a Core Web API为什么我在尝试将大文件发布到核心 Web API 时出现 404 【发布时间】:2018-06-07 09:15:31 【问题描述】:

我对@9​​87654321@ 和 Web API 之间的文件传输非常陌生,所以请原谅我的代码中的任何无知和猜测。我一直在尝试将使用System.IO.Compression.ZipFile 创建的文件发布到我的 Web API 大约一天,但总是收到 404 响应。如果我使用空流发布,则会调用 API 操作方法,因此我知道 404 是由于内容而不是 URI。

此方法位于尝试发布文件的客户端 WPF 应用程序中:

public async Task PostDirAsync(string localDirPath, string serverDir)

    var sourcePath = Path.Combine("Temp", Guid.NewGuid() + ".zip");
    ZipFile.CreateFromDirectory(localDirPath, sourcePath, CompressionLevel.Fastest, true);
    StreamContent streamContent;
    using (var fs = File.Open(sourcePath, FileMode.Open))
    
        var outStream = new MemoryStream();
        await fs.CopyToAsync(outStream);
        outStream.Position = 0;
        streamContent = new StreamContent(outStream);
    
    streamContent.Headers.Add("Content-Type", "application/octet-stream");
    var resp = await _client.PostAsync("api/File/PostDir?serverPath=WebUtility.UrlEncode(serverDir)", streamContent);

这是接收帖子的 Web API 中的操作方法,但前提是我在尝试发布之前不执行 outStream.Position = 0;

[HttpPost("PostDir")]
[DisableRequestSizeLimit]
public async Task<IActionResult> PostDir(string serverPath)
           
    var zipName = Path.Combine(_config["QuickDrive:TempDir"], Guid.NewGuid() + ".zip");
    using (var ms = new MemoryStream())
    using (var fileStream = System.IO.File.Create(zipName))
    
        await Request.Body.CopyToAsync(ms);
        ms.Position = 0;
        await ms.CopyToAsync(fileStream);
    
    return Ok();

action 方法被调用并在空流的情况下运行而没有错误,但由于它写入一个空文件,所以非常无用。我做错了什么?

【问题讨论】:

我希望当您调用ms.CopyToAsync(fileStream); 时,您将处于ms 流的末尾。在该行之前,您可能需要 ms.Position = 0; 之类的内容。 @KirkLarkin 不,仍然是一个空文件。 还有类似的情况,您将StreamContent 设置为可能已经在末尾的流。如果有帮助,您应该能够在调试器中看到这些流的位置和长度。也请查看Request.Body 流。 @KirkLarkin 看起来你是对的。当我在streamContent = new StreamContent(outStream); 之前执行outStream.Posiition = 0 时,我现在得到我的老朋友404 响应。 streamContent 之前似乎是空的,但现在它有内容,并且 web api 对象与该内容的类型有关。 【参考方案1】:

如 cmets 中所述,您的第一个问题是文件复制中涉及的 Stream 实例未使用 Stream.Position = 0 重置。我知道您已经进行了这些更改,但我只想强调这是一个由两部分组成的解决方案。

那么,第二部分:

在您的示例代码中,您添加了 [DisableRequestSizeLimit] 注释以绕过默认的 ASP.NET Core 2.0+ Kestrel 请求限制。但是,IIS 也有一个限制,默认为 30MB。当超出此大小限制时,IIS 本身会生成 404 响应,这就是您所看到的。

This answer 解释了如何使用自定义 Web.config 更改此限制(为了完整起见,包含在下面):

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <security>
      <requestFiltering>
        <!-- 1 GB -->
        <requestLimits maxAllowedContentLength="1073741824" />
      </requestFiltering>
    </security>
  </system.webServer>
</configuration>

顺便说一句:

除非您有特定的理由这样做,否则您可以避免在代码中使用MemoryStream,而只需将fs 直接传递给new StreamContent(...)。您可以对Request.Body 流执行类似操作,并将其直接复制到输出FileStream 中。这最终会是:

public async Task PostDirAsync(string localDirPath, string serverDir)

    var sourcePath = Path.Combine("Temp", Guid.NewGuid() + ".zip");
    ZipFile.CreateFromDirectory(localDirPath, sourcePath, CompressionLevel.Fastest, true);

    var streamContent = new StreamContent(File.Open(sourcePath, FileMode.Open));
    streamContent.Headers.Add("Content-Type", "application/octet-stream");
    var resp = await _client.PostAsync("api/File/PostDir?serverPath=WebUtility.UrlEncode(serverDir)", streamContent);

还有:

[HttpPost("PostDir")]
[DisableRequestSizeLimit]
public async Task<IActionResult> PostDir(string serverPath)
           
    var zipName = Path.Combine(_config["QuickDrive:TempDir"], Guid.NewGuid() + ".zip");
    using (var fileStream = System.IO.File.Create(zipName))
        await Request.Body.CopyToAsync(fileStream );
    return Ok();

【讨论】:

以上是关于为啥我在尝试将大文件发布到核心 Web API 时出现 404的主要内容,如果未能解决你的问题,请参考以下文章

为啥我在执行 doInBackground() 时出错

为啥我在尝试使用 fetch 连接 Api 时收到“错误请求”错误 400?

Docker容器上传到gcp cloud-run,核心Web api应用程序不起作用

IIS 上的 Angular 发布到 Asp.Net(非核心)Web Api 错误 415

将大文件 (2GB) 上传到 Autodesk Forge Data Management API

.net 核心 Web API - 415 不支持的媒体类型