#c# #asp.net-core #file-upload
#c# #asp.net-core #загрузка файла
Вопрос:
Я пытаюсь эффективно прокси-сервер для загрузки файлов через ASP.NET Контроллер Core 5 MVC для другого API:
[DisableFormValueModelBinding]
[HttpPost]
public async Task<IActionResult> Upload()
{
var reader = new MultipartReader(Request.GetMultipartBoundary(), Request.Body);
MultipartSection section;
while ((section = await reader.ReadNextSectionAsync().ConfigureAwait(false)) != null)
{
if (section.ContentType == "application/json")
{
await SendFile(section.Body);
}
}
return View("Upload");
}
private async Task SendFile(Stream stream)
{
var request = new HttpRequestMessage(HttpMethod.Post, "http://blah/upload");
request.Content = new StreamContent(stream);
var response = await httpClient.SendAsync(request);
}
Однако принимающий API всегда получает пустой поток.
Я могу подтвердить SendFile
, что метод работает, поскольку следующий тест работает из метода контроллера:
using (var fs = new FileStream("test.json", FileMode.Open))
{
await SendFile(fs);
}
И я могу увидеть загруженный файл, если попытаюсь прочитать его в контроллере:
var buf = new char[256];
using (var sr = new StreamReader(section.Body))
{
var x = await sr.ReadAsync(buf, 0, buf.Length);
while (x > 0)
{
log.Debug(new string(buf));
x = await sr.ReadAsync(buf, 0, buf.Length);
}
}
Таким образом, оба конца, похоже, работают, просто не вместе.
Я установил EnableBuffering
:
app.Use(next => context =>
{
context.Request.EnableBuffering();
return next(context);
});
И я отключаю привязку загруженных файлов к модели, используя DisableFormValueModelBindingAttribute
пример из Upload files в ASP.NET Ядро
Я также пытался перемотать поток вручную Seek
, но это не имеет значения.
Это работает, если я копирую его через MemoryStream
:
using (var ms = new MemoryStream())
{
await section.Body.CopyToAsync(ms);
await ms.FlushAsync();
ms.Seek(0, SeekOrigin.Begin);
await SendFile(ms);
}
Однако это буферизует файл в памяти, что не подходит для больших файлов.
Это также работает, если я сначала прочитаю загруженный файл, перемотаю назад, а затем попробую:
var buf = new char[256];
using (var sr = new StreamReader(section.Body))
{
var x = await sr.ReadAsync(buf, 0, buf.Length);
while (x > 0)
{
log.Debug(new string(buf));
x = await sr.ReadAsync(buf, 0, buf.Length);
}
}
section.Body.Seek(0, SeekOrigin.Begin);
// this works now:
await SendFile(section.Body);
Опять же, это не подходит для больших файлов.
Кажется, что поток находится не в том состоянии, в котором он должен использоваться моим SendFile
методом, но я не понимаю, почему.
Обновить
Основываясь на комментариях Джереми Лейкмана, я более внимательно посмотрел на то, что происходит с длиной потока.
Я обнаружил, что удаление EnableBuffering
заставляет его работать должным образом, поэтому проблема как бы решена этим.
Однако я наткнулся на этот комментарий aspnetcore Github, в котором участник заявляет, что:
Мы не поддерживаем передачу тела запроса в виде потока в HttpClient.
Этот и другие комментарии в этом выпуске поддерживают комментарии Джереми о CanSeek
и длине потока, и неясно (для меня), должно ли это действительно работать и является ли это просто совпадением, что это происходит сейчас (т. Е. Получу ли я еще одну ошибку позже).
В этом конкретном сценарии с MIME multipart, где мы не знаем длину потока без буферизации / подсчета всего файла, есть ли альтернатива StreamContent
или другой способ обработки загрузки файла?
Страница документов Microsoft загружает файлы в ASP.NET Core советует использовать только альтернативный подход. В нем говорится о потоковых загрузках, однако он останавливается на правильном использовании потока и просто буферизует файл в a MemoryStream
(полностью игнорируя цель потоковой передачи)
Комментарии:
1. Вы получаете исходный поток в виде фрагментированной передачи? В этом случае длина исходного потока будет недоступна, что может вызвать проблемы
StreamContent
.2. @JeremyLakeman нет, запрос не обрабатывается. Длина содержимого правильно отправляется и фиксируется в заголовках запроса. Длина потока равна 0 до получения доступа и является ожидаемым значением после обработки потока в обоих случаях (т. Е. Успешное тестовое чтение и неудачная последующая публикация).
3. Я не использовал
MultipartReader
, это многокомпонентный mime? с граничными строками? Да, невозможноsection.Body.Length
установить известное значение до чтения потока. ПоэтомуStreamContent
, скорее всего, отображается 0, даже если данные доступны. Ifsection.Body.CanSeek
thenStreamContent
, вероятно, сделает что-то не так.