Как определить, с какого куска файла нужно продолжить загрузку после обрыва соединения
Что проверяют
Понимаете ли вы, что позиция возобновления определяется не счётчиком в памяти, а тем, что реально лежит на диске или на сервере, и что это нужно сверять. Уточняют обычно два момента: как узнать, что файл на сервере не изменился за время обрыва, и как быть с отправкой файла на сервер, а не скачиванием.
Как отвечать
Для скачивания по HTTP опорные факты такие:
- смещение: берите размер локального частичного файла,
new FileInfo(path).Length. Надёжнее качать во временный файл и переименовывать по завершении, а при кусках фиксированного размера обрезать хвост до последнего проверенного куска: при обрыве последний блок мог записаться не полностью; - запрос:
HttpClientиrequest.Headers.Range = new RangeHeaderValue(offset, null).HttpWebRequestиWebRequest.Createв современном .NET помечены устаревшими, в новых проектах их не используют; - проверка ответа: код 206 и
response.Content.Headers.ContentRangeподтверждают, что сервер отдал нужный диапазон. Ответ 200 значит, чтоRangeпроигнорирован и пришёл весь файл, тогда пишете с нуля. Поддержку диапазонов видно поAccept-Ranges: bytes; - неизменность: сохраните
ETagилиLast-Modifiedиз первого ответа и передавайте вIf-Range. Если файл сменился, сервер вернёт 200 с новым содержимым вместо склейки двух версий; - поток:
HttpCompletionOption.ResponseHeadersReadиCopyToAsyncвFileStream, открытый на дозапись, чтобы не держать файл в памяти.
Для отправки на сервер источник истины сам сервер. В протоколе tus клиент делает HEAD и получает Upload-Offset. В S3 multipart upload принятые части отдаёт ListParts, в Azure Blob Storage незафиксированные блоки видны в списке блоков. В своём API делайте так же: эндпоинт статуса загрузки и хэш каждого куска.
Где спотыкаются
Хранят смещение в переменной или отдельном файле и не сверяют его с фактическим размером. После падения процесса они расходятся.
Не проверяют код 206 и дописывают весь файл в конец частичного. Размер получается больше нужного, а содержимое битое.
Открывают файл через FileMode.Append, когда начинают с нуля. Старый частичный файл остаётся, и данные задваиваются.