web-dev-qa-db-ja.com

大きなファイルをCore Web APIに投稿しようとすると404になるのはなぜですか

私はHttpClientとWeb APIの間のファイル転送に非常に慣れていないので、コードの無知や当て推量を許してください。私はSystem.IO.Compression.ZipFileで作成されたファイルをWeb APIに約1日投稿しようとしていますが、常に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();
}

アクションメソッドが呼び出され、空のストリームでエラーなしで実行されますが、空のファイルを書き込むため、ほとんど役に立ちません。何が悪いのですか?

12
ProfK

コメントで述べたように、最初の問題は、ファイルのコピーに関係するStreamインスタンスが_Stream.Position = 0_を使用してリセットされていないことでした。すでにこれらの変更を行っていることは承知しておりますが、これは2つの部分からなるソリューションであることを強調したいだけです。

したがって、2番目の部分:

サンプルコードでは、デフォルトのASP.NET Core 2.0+ Kestrelリクエストの制限を回避するために_[DisableRequestSizeLimit]_アノテーションを追加しました。ただし、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>
_

余談ですが、

特定の理由がない限り、コードで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();
}
_
12
Kirk Larkin