为什么我会尝试将404大文件发布到Core Web API

Pro*_*ofK 12 c# dotnet-httpclient kestrel-http-server asp.net-core asp.net-core-2.0

我是一个非常新的文件传输HttpClient和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);
}
Run Code Online (Sandbox Code Playgroud)

这是接收帖子的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();
}
Run Code Online (Sandbox Code Playgroud)

调用action方法并且在没有错误的情况下使用空流运行,但在写入空文件时非常无用.我究竟做错了什么?

Kir*_*kin 12

我花了一些时间为你研究这个,我相信我已经明白了.正如评论中所提到的,您的第一个问题是Stream文件复制中涉及的实例未使用重置Stream.Position = 0.我知道你已经做了这些改变,但我只想强调这是一个由两部分组成的解决方案.

那么,第二部分:

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

此答案解释了如何使用自定义更改此限制Web.config(下面包含完整性):

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <security>
      <requestFiltering>
        <!-- 1 GB -->
        <requestLimits maxAllowedContentLength="1073741824" />
      </requestFiltering>
    </security>
  </system.webServer>
</configuration>
Run Code Online (Sandbox Code Playgroud)

有点侧面说明:

除非您有特定的理由这样做,否则您可以避免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);
}
Run Code Online (Sandbox Code Playgroud)

与:

[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();
}
Run Code Online (Sandbox Code Playgroud)