Как сервис должен определять порядок загрузки частей файла: последовательно или хаотично
Что проверяют
Понимаете ли вы, что порядок прихода частей по сети гарантировать нельзя и сервис не должен от него зависеть. Следом обычно спрашивают, как докачать файл после обрыва и как убедиться, что собранный файл целый.
Как отвечать
Главная мысль: порядок определяет не очередь прихода, а метаданные части. Клиент заранее получает идентификатор загрузки, делит файл на куски и в каждом запросе передаёт номер части или смещение. Дальше сервис принимает части в любом порядке:
- параллельная загрузка: клиент шлёт несколько частей одновременно, например через
Parallel.ForEachAsyncс ограничениемMaxDegreeOfParallelism. Так работает multipart upload в S3-совместимых хранилищах: части нумеруются, грузятся в любом порядке, а финальный запрос перечисляет их для сборки; - запись по смещению: на сервере кусок пишется сразу на своё место через
RandomAccess.WriteAsync(handle, buffer, offset)или во временные файлы с последующей склейкой; - учёт принятых частей: сервис хранит, какие номера уже получены. Это нужно и для докачки (клиент спрашивает, чего не хватает), и для идемпотентности: повтор той же части перезаписывает её, а не дописывает второй раз;
- финализация: отдельный запрос «загрузка завершена», проверка, что пришли все части, и сверка контрольной суммы, например SHA-256 от клиента.
Строго последовательная загрузка оправдана, когда сервер обрабатывает поток на лету: считает хэш инкрементально, пережимает видео, пишет в хранилище без произвольного доступа. Так устроен базовый протокол tus: сервер принимает только кусок с текущим Upload-Offset.
Где спотыкаются
Полагаются на порядок прихода. Два параллельных HTTP-запроса легко приходят наоборот, и файл собирается битым.
Не делают повтор безопасным. После таймаута клиент шлёт часть снова, сервер дописывает её в конец, и размер файла уже не сходится.
Забывают про брошенные загрузки и лимиты. Незавершённые части копятся на диске без правила очистки, а попытка принять весь файл одним запросом упирается в MaxRequestBodySize Kestrel, по умолчанию около 28,6 МБ.