Загрузка и скачивание файлов, стриминг

↑ 3.3 Web API: контроллеры, Minimal API, валидация · ← Предыдущая

Обязательно~3 мин чтенияУровень: middle

Зачем это на собесе

Практический вопрос: как принять/отдать большой файл и не «съесть» память.

Объяснение

Загрузка

app.MapPost("/upload", async (IFormFile file, IStorage storage, CancellationToken ct) =>
{
    if (file.Length > 20_000_000) return Results.BadRequest("too big");
    await using var s = file.OpenReadStream();
    var key = await storage.SaveAsync(s, file.ContentType, ct);
    return Results.Ok(new { key });
}).DisableAntiforgery();

Для больших файлов IFormFile буферизует (в память до 64 КБ, потом во временный файл). Для стриминга без буфера читают HttpContext.Request.Body или MultipartReader.

Скачивание

app.MapGet("/files/{key}", async (string key, IStorage storage, CancellationToken ct) =>
{
    var (stream, contentType, name) = await storage.OpenAsync(key, ct);
    return Results.File(stream, contentType, name, enableRangeProcessing: true);
});

enableRangeProcessing включает Range-запросы (докачка, видео). Для отдачи из объектного хранилища (MinIO/S3) лучше выдавать pre-signed URL, чтобы файл шёл мимо приложения.

Стриминг данных

app.MapGet("/export", async (IAsyncEnumerable<Row> rows) => Results.Ok(rows));   // JSON-массив потоком
app.MapGet("/events", (CancellationToken ct) => TypedResults.ServerSentEvents(Ticks(ct)));

Нюансы и подводные камни

  • Лимиты: Kestrel MaxRequestBodySize, FormOptions.MultipartBodyLengthLimit, лимиты proxy (nginx client_max_body_size).
  • Проверяйте тип файла по содержимому (magic bytes), а не по расширению/Content-Type.
  • Не используйте имя файла клиента как путь (path traversal).
  • Антивирусная проверка и карантин для пользовательских загрузок.
  • Не загружайте файл целиком в byte[]: OOM на больших файлах.

Практика

  1. Реализуйте загрузку 1 ГБ файла потоком с постоянным потреблением памяти.
  2. Отдайте pre-signed URL из MinIO вместо проксирования.
  3. Добавьте поддержку Range и проверьте докачку curl -C -.

Вопросы с ответами

Связанные темы