Почему я получаю 404, пытающихся отправить большой файл в Core Web API - PullRequest
0 голосов
/ 07 июня 2018

Я очень плохо знаком с передачей файлов между HttpClient и веб-API, поэтому прошу прощения за любое невежество и догадки в моем коде.Я пытался опубликовать файл, созданный с помощью System.IO.Compression.ZipFile, в своем веб-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);
}

И это метод действия в веб-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();
}

Метод действия вызывается и запускается без ошибок с пустым потоком, но довольно бесполезен, поскольку записывает пустой файл.Что я делаю не так?

1 Ответ

0 голосов
/ 07 июня 2018

Как упоминалось в комментариях, ваша первая проблема заключалась в том, что экземпляры Stream, связанные с копированием файлов, не сбрасывались с помощью Stream.Position = 0.Я знаю, что вы уже внесли эти изменения, но я просто хочу подчеркнуть, что это решение состоит из двух частей.

Итак, вторая часть:

В вашем примере кода вы 'Мы добавили аннотацию [DisableRequestSizeLimit], чтобы обойти ограничения по умолчанию для запросов Kestrel ASP.NET Core 2.0+.Тем не менее, IIS также устанавливает ограничение, которое по умолчанию составляет 30 МБ.Когда этот предел размера превышен, сам 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();
}
...