为啥我在尝试将大文件发布到核心 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 【问题描述】:我对@987654321@ 和 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的主要内容,如果未能解决你的问题,请参考以下文章
为啥我在尝试使用 fetch 连接 Api 时收到“错误请求”错误 400?
Docker容器上传到gcp cloud-run,核心Web api应用程序不起作用
IIS 上的 Angular 发布到 Asp.Net(非核心)Web Api 错误 415