Od tématu magazínu k projektové praxi
Vhodné stránky služeb a technické stránky k příspěvku
Scéna je nepříjemná, ale typická: noční úloha stahuje velký archiv, ukazatel průběhu stojí na 93 %. Pak krátký VPN výkyv, timeout, přerušení. Ráno leží v cílovém adresáři soubor, velikost „sedí“, ale při rozbalování se objeví jen „unexpected end of file“. Kdo pak reflexivně doplní „Resume“, rychle narazí na další okrajový případ: stahování proběhne, ale je složené ze dvou verzí.
Právě pro to je určen HttpClient Download mit Resume – ale pouze pokud s ním budeš zacházet jako s malou datovou pipeline: s jasnými pravidly, kdy můžeš připojovat, jak ověřovat věrnost verze (ETag/If-Range) a jak spravovat lokální mezistavy (Partial Files) tak, aby provoz a debugování nebyly hrou na odhad.
Cílový konflikt se dá jasně pojmenovat: šetření šířky pásma a času (pokračování místo restartu) stojí proti integritě a udržovatelnosti (žádné tiché mixování verzí, rozhodnutí musí být sledovatelná, reprodukovatelný chybový stav). Tento článek je postaven jako karta zvážení: přínosy, náklady, rizika, limity – a pragmatická linie, kdy Resume skutečně implementovat a kdy je lepší kvalitní retry.
Abwägungskarte: Zwei Ziele, ein harter Trade-off
Resume se nevyplatí proto, že je „moderní“, ale protože ti to okolnosti vynutí: velké soubory, nestabilní linky, časová okna v batchi, uživatelé, kteří stahování přeruší, nebo služby, které po restartu mají pokračovat tam, kde skončily. Současně je Resume místo, kam si nevědomky vnesete datovou korupci – a ta se často projeví až mnohem později (import selže, podpis neplatný, instalace se nespustí).
| Situace | Resume-Nutzen | Typisches Risiko | Rozumné rozhodnutí |
|---|---|---|---|
| Velké soubory přes WAN/VPN nebo mobilní sítě | vysoký (čas, šířka pásma) | „200 statt 206“ je připojeno naslepo | Resume ano, ale defenzivně (Status/Content-Range/Validator) |
| „latest“-URL / artefakty bez vázání na verzi | střední | směs verzí po meziaktualizaci | Resume jen se silným validátorem, jinak restart |
| Interní storage se stabilními ETagy | vysoký | nízké, pokud jsou hlavičky konzistentní | Resume velmi vhodné, metadat persistovat |
| Malé soubory (několik desítek MB) | nízký | komplexita převažuje | plné stažení + retry/backoff je většinou lepší |
| Proměnlivé proxy/CDN, nejasná situace s hlavičkami | nejasný | nekonzistentní validátory, těžké debugování | pouze s dobrým logováním; jinak záměrně konzervativní |
Proč obnovení stahování (Resume) je víc než „nastavení Range“
HTTP zná Byte-Range-Requests přes hlavičku Range. Důležitá, často přehlížená věta: podpora Range je volitelná. Server může Range ignorovat a odpovědět, jako by šlo o běžný GET – tedy 200 OK s celým tělem. Pro klienta to znamená: pravda je v Response, ne ve tvém záměru v Requestu.[Quelle]
Klient pro obnovení stahování, který „poslal Range“ zamění za „přijde část obsahu“, dělá přesně tu chybu, kterou jsi viděl na začátku scény: celý download se připojí k již částečně staženému souboru. Výsledkem je, že soubor vypadá dost velký, ale logicky je poškozený.
Signál úspěchu: 206 plus věrohodný Content-Range
Když obnovení skutečně funguje, uvidíš typicky:
- Stav
206 Partial Contentmísto200 OK Content-Ranges Range-Unitbytesa očekávaným startovacím offsetem- konzistentní validátory (např. ETag), pokud je používáš k ověření verze
Tyto kontroly nejsou „nice to have“. RFC 9110 explicitně stanoví, že příjemce nesmí odpověď 206 s neznámou Range-Unit skládat s uloženou reprezentací. Praktický důsledek: bez korektně interpretovaného Content-Range žádné připojování (append).[Quelle]
If-Range a ETag: čistý postup proti míchání verzí
Jádro problému při pokračování není technika připojování, ale otázka: Stahuješ stále tentýž zdroj? HTTP poskytuje pro tento účel validátory. ETag (Entity Tag) je nejpřesnější páka: server-side identifikátor konkrétní reprezentace. Last-Modified je datum, často užitečné, ale méně ostré (časové rozlišení, repackaging, různé mezistupně přes CDN).
Mechanismus „Range pouze pokud jde opravdu o stejnou verzi“ se jmenuje If-Range: pošleš Range plus podmínku. Pokud validátor sedí, dostaneš Range; pokud nesedí, má server poslat kompletní zdroj. U ETagů platí, že podmínka je splněna pouze při silném ETag-matchi (strong comparison). To není věc interpretace, ale tak to standard předepisuje.[Quelle]
Provozní realita zůstává: pokud server Range nepodporuje, musí If-Range ignorovat. Zní to akademicky, ale je to přímé zadání pro tvůj klient: musíš počítat i s 200 OK a z toho vyvodit tvrdé rozhodnutí (restartovat stahování, ne připojovat).[Quelle]
Strategie částečných souborů: lokální protějšek HTTP sémantiky
Obnovení znamená: spravuješ lokální stav. A tento stav musí zůstat konzistentní i po pádu, restartu, paralelním spuštění nebo uživatelské akci („znovu kliknout“). Čistá strategie částečných souborů se skládá ze tří artefaktů:
- Částečný soubor jako jediný zápisový cíl během stahování (např.
foo.zip.part) - Metadata uložena odděleně (např.
foo.zip.part.meta.json): URL, čas, ETag, Last-Modified, očekávaná délka (pokud známá), případně Content-Type, a lokální délka při posledním čistém flushu - Atomické dokončení: až po úplném stažení a kontrole konzistence bude přejmenováno/nahrazeno na finální název
Proč metadata? Bez nich při dalším spuštění znáš jen délku souboru – ale nevíš, zda odpovídá právě této verzi na serveru. Právě tato informace rozhoduje, zda můžeš pokračovat.
HttpClient Download mit Resume: Entscheidungslogik, die du testen kannst
System.Net.HttpClient ti poskytuje Requests, Responses a Streams. Robustnost plyne z tvé rozhodovací matice. Pokud ji jasně sformuluješ, můžeš ji také jasně otestovat (včetně „Server ignoriert Range“, „Validator mismatch“, „416“).
Minimální postup ve 6 krocích
- Zjištění lokálního stavu: Existuje
.part? Přečíst délku jako offset. Načíst metadata. - Rozhodnout o možnosti Resume: Pokus o Resume jen pokud je Offset > 0 a v metadatech je alespoň jeden použitelný Validator (ETag bevorzugt).
- Sestavit request: Nastavit Range od offsetu. Navíc nastavit
If-Range(ETag, jinak Last-Modified). - Klasifikovat response:
206:Content-Rangeparsovat, Range-Unitbyteszkontrolovat, startovací offset musí přesně sedět. Pouze pak Append.200: nikdy nepřipojovat. Stav částečného souboru odstranit nebo izolovat (karanténa), stáhnout znovu.416 Range Not Satisfiable: Offset už nesedí (lokálně nebo na serveru). Resetovat stav a znovu začít.- jinak: chybová cesta s jasnou diagnózou (logovat Statuscode + Header).
- Zapsat do streamu: po blocích, pravidelně flushovat, aktualizovat metadata (např. „poslední flush při délce X“).
- Dokončení: Kontrola velikosti/konzistence (pokud možno). Poté atomicky přejmenovat/nahrazit.
RangeHeaderValue: kein Detail, sondern Fehlerprävention
.NET nabízí pro hlavičku Range typovanou třídu RangeHeaderValue. To není jen „krásný kód“: snižuje syntaktické chyby v hlavičkách a dělá logy srozumitelnější, protože generuje konzistentní hodnoty. Microsoft při tom dokumentuje i provozně relevantní hranici: servery nemusí podporovat Range-Requests; Accept-Ranges je indikátor, ne záruka.[Quelle]
Složený příklad: když „200 OK“ zničí Resume
Typické úskalí a jak je v logu rozpoznáš
Accept-Ranges: užitečný signál, ale bez záruky
Mnoho týmů používá Accept-Ranges jako indikaci „server podporuje resume“. MDN popisuje hlavičku přesně takto jako indikátor podpory („vše kromě none„). To je praktické pro diagnostiku a telemetrii. Přesto: nikdy se neredukuj jen na to. Proxy, auth-gateway nebo CDN mohou chování změnit. Tvoje logika proto musí vždy vycházet ze status kódu (206 vs. 200) a z Content-Range, ne z nadějného signálu v Accept-Ranges.[Zdroj]
Slabé ETagy (W/…) a transformace
Slabé ETagy (W/"…") se objevují častěji, než bychom si přáli. Pro If-Range je ale rozhodující silná shoda ETagu. V praxi to znamená: pokud dostaneš pouze weak ETag, je resume méně spolehlivé. Můžeš jej nadále použít, ale měl bys postupovat konzervativněji (např. při nejmenších nesrovnalostech restartovat) a u zvlášť kritických souborů doplnit out-of-band kontrolu integrity (hash z manifestu/API), pokud je dostupná.
Neznámá celková délka (chunked) a chybějící ověřitelnost
Pokud nepřichází Content-Length (např. chunked transfer), ztrácíš jednoduchou kontrolu dokončení „lokální délka == očekávaná délka“. Resume může přesto fungovat, ale potřebuješ spolehlivé validátory a kvalitní logging. Bez těchto opor bude hledání chyb zdlouhavé, protože ani nepoznáš, zda ti chybí bajty nebo jich je příliš.
Paralelizace: až když máš základy stabilní
Načítat více rozsahů paralelně je možné, ale násobí to problémy se stavem: správa segmentů, náhodné zápisy, slučování, protokolování chyb, retry pro segment. Pro mnoho procesně blízkých softwarových řešení je to přehnané. Stabilní zisk často přinese už sekvenční download + resume + jasná rozhodovací matice. Paralelizace se vyplatí až tehdy, když máš prokázanou stabilní podporu Range a konkrétní měření ukážou, že omezení skutečně leží v síti nebo na serveru.
Debugging, který přinese jasno za 5 minut
Chyby při obnovení přenosu jsou zřídkakdy „jen špatný if“. Obvykle jde o kombinaci infrastruktury (Proxy/CDN), konfigurace serveru, timeoutů a lokálního stavu. Proto se vyplatí strukturované protokolování pro každý pokus. Ne jako textová zeď, ale jako konzistentní pole:
- Požadavek: URL (bez tajných údajů), metoda, Range (start/konec),
If-Range(ETag nebo datum). - Odpověď: Stavový kód,
Accept-Ranges,ETag,Last-Modified,Content-Length,Content-Range(surový a parsovaný). - Lokální stav: cesta k části (.part), délka části před požadavkem, režim zápisu (Append/Overwrite), délka po zápisu.
- Rozhodnutí: „Resume accepted“, „Resume rejected (200) – RESTart“, „416 – reset“, „Validator mismatch – RESTart“.
Pomocí právě těchto dat můžeš testovat hypotézy: „Server ignoruje Range?“, „Mění se ETag mezi požadavky?“, „Je offset špatně určen?“, „Zapisuje současně druhý proces?“ – aniž bys musel vytahovat Wireshark.
Kdy vědomě neimplementovat obnovení
Nejčistší architektonické rozhodnutí může být někdy nezařadit obnovení – nikoli ze snadnosti, ale protože ve tvém scénáři přináší více povrchů pro chyby než užitku:
- URL bez vázání na verzi: Pokud se pod stejnou URL pravidelně objevuje nový obsah, je obnovení bez silného validátoru riziko integrity.
- Žádné použitelné validátory: Pokud ani ETag ani Last-Modified nejsou konzistentní, je „pokračování“ sázkou. U kritických importů je to často horší než kontrolovaný RESTart.
- Stažení malé a časté: Zde jsou přehlednější retry strategie (timeout, backoff, případně mirror-URL) udržovatelnější a v provozu snáze vysvětlitelné.
Pragmatická bezpečnostní síť, která se vyplatí
Karanténa místo mazání
Pokud se pokus o resume nezdaří, neodstraňuj reflexivně .part. Přejmenuj ji (např. .part.bad + časové razítko). To odkryje problémy se serverem/proxy, protože později můžeš porovnat response hlavičky se stavem souboru. A zabráníš tomu, aby vzácný problém „úklidem“ přímo zmizel z analýzy.
Zamykání proti současným startům
Pokud dva procesy současně zapisují do téže .part, jsou všechny HTTP validátory bezcenné. Exkluzivní lock (file lock nebo systémový mutex) není proto luxus, ale základní ochrana. Metadata mohou navíc nést informaci o vlastníkovi (ID instance), aby jsi mohl „opuštěná“ stahování spolehlivě rozpoznat a uklidit.
Krátká pravidla, která opravdu potřebuješ
- Připojovat (Append) pouze při
206– a jen s ověřenýmContent-Range(jednotkabytes, počáteční offset sedí). 200znamená: začít znovu (overwrite), nikdy nepřipojovat k částečnému souboru.If-Rangesnižuje míchání verzí, ale nenahrazuje povinnost vyhodnotit stavový kód aContent-Range.416je signál resetu: lokální offset už neodpovídá, stav zahodit/umístit do karantény.
Závěr: nejdřív integrita, pak optimalizace
Stahování pomocí HttpClient s podporou obnovení je dobré tehdy, když je nudné: nastaví Range jen pokud je lokální stav spolehlivý, naváže obnovení pouze při odpovědi 206 a ověřeném Content-Range, a zabrání míchání verzí přes If-Range a validátory. Všechno ostatní jsou „vypadá rychle“ funkce, které ti později snědí čas, protože chyby jsou vzácné a pak těžko reprodukovatelné.
Pokud chceš pro updatery, importní toky nebo služby pečlivě nastavit robustní download-pipeline (Resume, Logging, Partial-File-Strategie, Betriebskanten), dá se to často vyřešit krátkou architektonickou kontrolou – včetně otázky, zda se složitost ve tvém prostředí vůbec vyplatí. Kontaktuj a projednej záměr s Net-Base.
Zdroje a další informace
Hlavní odborná tvrzení byla redakčně zařazena na základě následujících externích zdrojů.
- RFC 9110: HTTP Semantics (www.rfc-editor.org)
HTTP Range-Requests jsou volitelné; servery mohou Range ignorovat a odpovědět jako při běžném GET, proto musí klient vyhodnotit statusový kód a hlavičky místo slepého navázání. - HTTP range requests – HTTP | MDN (developer.mozilla.org)
Accept-Ranges je signál podpory Range, ale klienti musí robustně kontrolovat chování odpovědi (206/Content-Range). - RangeHeaderValue Class (System.Net.Http.Headers) | Microsoft Learn (learn.microsoft.com)
.NET poskytuje třídou RangeHeaderValue typizovanou reprezentaci hlavičky Range; servery nemusí podporovat Range-Requests.
další krok
Když se z tématu stane reálný projekt, měly by být architektura, stávající systém a provoz posuzovány společně již v rané fázi.
Podporujeme nejen při jednotlivých otázkách, ale i v případě, že se z útržků zdrojového kódu, legacy témat nebo nápadů na portál má vyvinout robustní podnikový projekt.
- Současný stav, cílový stav a technická rizika jsou hodnoceny společně.
- REST, přístup k datům, portály a rollout nebudou přesunuty do pozdějších fází.
- Včas zjistíte, která varianta je ekonomicky i provozně životaschopná.