Net-Base Magazín

System.Net.HttpClient: Download mit Resume – Range-Requests, ETag-Prüfung und saubere Partial-File-Strategie

Wenn Downloads abbrechen, ist Resume verlockend – und riskant. So kombinierst du Range-Requests, If-Range/ETag und eine robuste .part-Strategie, ohne stille Dateikorruption zu bauen.

Legacy. Refaktorovanie. Migrácia.

Modernizovať vyrastený softvér bez straty odbornej podstaty systému.

Zastaralé systémy Delphi Refaktorovanie Uvedenie do prevádzky

Zachovať doménovú logiku

Hodnotná obchodná logika zostáva viditeľná a nie je zakopaná pod kompletnou prestavbou.

Dekouplovanie kódovej bázy

Oddeliť zodpovednosti, aby sa nové rozhrania a nové služby mohli kontrolovane pripojiť.

Zabezpečiť plánovateľnosť migrácie

Postupné dodávanie, testovanie a zavádzanie do prevádzky namiesto rizikového ostrého prechodu.

24.10.2026

Od témy magazínu k projektovej praxi

Súvisiace stránky služieb a technológií k príspevku

Scéna je nepríjemná, ale typická: nočný job nahráva veľké archívum, ukazovateľ priebehu stojí na 93 %. Potom krátky zásek VPN, timeout, prerušenie. Ráno je v cieľovom adresári súbor, veľkosť „sedí“, ale pri rozbaľovaní sa objaví len „unexpected end of file“. Kto potom reflexívne doplní „pokračovanie“, rýchlo narazí na ďalší okrajový prípad: download beží ďalej, no je zložený z dvoch verzií.

Presne na to slúži HttpClient Download s pokračovaním – ale iba ak s ním zaobchádzaš ako s malým dátovým pipeline: s jasnými pravidlami, kedy môžeš pripájať, ako kontroluješ verziu (ETag/If-Range) a ako spravuješ lokálne medzistavy (čiastočné súbory), aby prevádzka a debugovanie neboli lotériou.

Cieľový konflikt sa dá jasne pomenovať: úspora šírky pásma a času (pokračovať namiesto začať znova) stojí proti integrite a udržiavateľnosti (žiadny tichý zmix verzií, zrozumiteľné rozhodnutia, reprodukovateľný chybový stav). Tento článok je postavený ako rozhodovacia mapa: prínosy, náklady, riziká, limity – a pragmatická línia, kedy pokračovanie skutočne implementovať a kedy je lepší dobrý retry.

Rozhodovacia mapa: Dva ciele, tvrdý trade-off

Pokračovanie sa neoplatí preto, že je „moderné“, ale preto, že to prinúti tvoje prostredie: veľké súbory, nestabilné linky, časové okná v batchi, používatelia, ktorí prerušíšťahovanie, alebo servisy, ktoré po reštarte majú pokračovať tam, kde prestali. Zároveň je práve pokračovanie miesto, kde si nevšimnuteľne zavlečieš korupciu dát – a tá sa často prejaví až omnoho neskôr (import zlyhá, podpis neplatí, setup sa nespustí).

Situácia Výhoda pokračovania Typické riziko Pravdepodobné rozhodnutie
Veľké súbory cez WAN/VPN alebo mobilné siete vysoká (čas, šírka pásma) „200 namiesto 206“ sa nahodí naslepo Pokračovanie áno, ale defenzívne (Status/Content-Range/Validator)
„latest“-URL / artefakty bez verziovej väzby stredná mix verzií po medziaktualizácii Pokračovanie len s silným validátorom, inak reštart
Interné úložisko so stabilnými ETagmi vysoká nízke, ak sú headery konzistentné Pokračovanie veľmi vhodné, perzistovať metadata
Malé súbory (niekoľko desiatok MB) nízka komplexita preváži Plný download + retry/backoff je zvyčajne lepší
Meniace sa proxy/CDN, nejasná situácia s headermi nejasné nekonzistentné validátory, ťažké debugovanie Iba s dobrým loggingom; inak vedome konzervatívne

Prečo je obnovenie viac než „nastavenie Range“

HTTP pozná požiadavky Byte-Range cez hlavičku Range. Dôležitá, často prehliadaná veta: podpora Range je voliteľná. Server môže Range ignorovať a odpovedať tak, akoby to bol obyčajný GET – teda s 200 OK a plným telom odpovede. Pre klientov to znamená: pravda je v Response, nie vo vašom zámere v Requeste.[Zdroj]

Klient na obnovenie, ktorý „odoslal Range“ zamieňa s „príde čiastočný obsah“, robí presne tú chybu, ktorú ste videli na začiatku scény: celý download sa pripojí k už čiastočne stiahnutému súboru. Výsledok: súbor vyzerá byť dosť veľký, ale je logicky poškodený.

SIGNAL úspechu: 206 plus plausible Content-Range

Keď obnovenie skutočne funguje, typicky uvidíte:

  • Stav 206 Partial Content namiesto 200 OK
  • Content-Range s Range-Unit bytes a očakávaným počiatočným offsetom
  • konzistentné validátory (napr. ETag), ak ich používate na zachovanie verzie

Kontroly nie sú „pekne mať“. RFC 9110 explicitne stanovuje, že príjemcovia nesmú zostavovať uloženú reprezentáciu z 206-odpovede s neznámou Range-Unit. Praktický dôsledok: bez správne pochopeného Content-Range žiadne pripojenie (append).[Zdroj]

If-Range a ETag: čistý spôsob proti miešaniu verzií

Jadro problému pri pokračovaní nie je technika pripojenia, ale otázka: sťahujete stále tú istú zdrojovú reprezentáciu? HTTP na to poskytuje validátory. ETag (Entity Tag) je pritom najpresnejší páka: serverom generovaný identifikátor konkrétnej reprezentácie. Last-Modified je dátum, často použiteľný, ale menej ostrý (rozlíšenie času, repackaging, rôzne medzi-stupne cez CDN).

Mechanizmus „Range len ak je to naozaj tá istá verzia“ je If-Range: odošlete Range plus podmienku. Ak validátor súhlasí, dostanete Range; ak nesúhlasí, má server poslať kompletnú zdrojovú reprezentáciu. Pri ETagoch platí, že podmienka je splnená iba pri silnom zhode ETagu (strong comparison). Toto nie je vec interpretácie, ale presne tak to štandard definuje.[Zdroj]

Prevádzková realita zostáva: ak server Range nepodporuje, musí If-Range ignorovať. Znie to akademicky, ale je to priama požiadavka na vášho klienta: musíte rátať aj s 200 OK a z takej odpovede vyvodiť jednoznačné rozhodnutie (znovu začať, nepripájať).[Zdroj]

Strategia čiastočných súborov: lokálny protiklad k HTTP-semantike

Obnovenie znamená: spravujete lokálny stav. A tento stav musí byť stabilný aj po páde, reštarte, paralelnom spustení alebo akcii používateľa („znovu kliknúť“). Čistá stratégia čiastočných súborov pozostáva z troch artefaktov:

  • Partial-súbor ako jediné miesto zápisu počas sťahovania (napr. foo.zip.part)
  • Metadaten uložené oddelene (napr. foo.zip.part.meta.json): URL, časový okamih, ETag, Last-Modified, očakávaná dĺžka (ak známa), prípadne Content-Type, ako aj lokálna dĺžka pri poslednom čistom Flush
  • Atomárne ukončenie: až po úplnom stiahnutí a kontrolnom teste platnosti sa premenuje/ nahradí na finálny názov

Prečo metadáta? Inak pri ďalšom spustení poznáš len dĺžku súboru – nie však, či patrí práve tejto verzii na serveri. Práve táto informácia rozhoduje, či môžeš pokračovať.

HttpClient sťahovanie s možnosťou pokračovania: rozhodovacia logika, ktorú môžeš testovať

System.Net.HttpClient ti poskytuje Requests, Responses a Streams. Robustnosť vychádza z tvojej rozhodovacej matice. Ak ju jasne sformuluješ, môžeš ju aj spoľahlivo testovať (vrátane „Server ignoruje Range“, „Validator mismatch“, „416″).

Minimálny postup v 6 krokoch

  1. Zistiť lokálny stav: Existuje .part? Načítať dĺžku ako offset. Načítať metadáta.
  2. Rozhodnúť o možnosti pokračovania: Pokračovanie skúsiť len, ak je Offset > 0 a v metadátach je aspoň jeden použiteľný validator (preferovaný ETag).
  3. Vytvoriť request: Nastaviť Range od offsetu. Dodatočne nastaviť If-Range (ETag, inak Last-Modified).
  4. Klasifikovať response:
    • 206: parsovať Content-Range, overiť Range-Unit bytes, počiatočný offset musí presne sedieť. Iba potom pripojiť.
    • 200: nikdy nepripájať. Stav partial vyhodiť alebo dať do karantény, stiahnuť znova.
    • 416 Range Not Satisfiable: offset už nesedí (lokálne alebo na strane servera). Resetovať stav a začať odznova.
    • inak: chybová cesta s jasnou diagnostikou (logovať statusový kód + hlavičky).
  5. Písať do streamu: po blokoch, pravidelne flushovať, aktualizovať metadáta (napr. „posledný flush pri dĺžke X“).
  6. Ukončenie: kontrola veľkosti/platnosti (ak je to možné). Potom atomicky premenovať alebo nahradiť.

RangeHeaderValue: nie detail, ale prevencia chýb

.NET poskytuje pre hlavičku Range typovú triedu RangeHeaderValue. To nie je len „pekný kód“: znižuje syntaktické chyby v hlavičkách a robí logy ľahšie interpretovateľnými, pretože vytvára konzistentné hodnoty. Microsoft pritom dokumentuje aj prevádzkovú hranicu relevantnú pre prax: servery nemusia podporovať Range-requests; Accept-Ranges je indikátor, nie záväzok.[Quelle]

Konštruovaný príklad: keď „200 OK“ rozbije tvoje pokračovanie

Typické úskalia a ako ich rozpoznáš v logu

Accept-Ranges: užitočný signál, ale žiadna záruka

Mnohé tímy používajú Accept-Ranges ako indikátor „server podporuje resume“. MDN popisuje tento header presne takto ako indikátor podpory („všetko okrem none„). To je praktické pre diagnostiku a telemetriu. Napriek tomu: nikdy sa nerozhoduj len na jeho základe. Proxy, autentifikačný gateway alebo CDN môžu správanie zmeniť. Tvoja logika sa preto musí vždy riadiť stavovým kódom (206 vs. 200) a Content-Range, nie zábleskom nádeje v Accept-Ranges.[Zdroj]

Weak ETags (W/…) a transformácie

Weak ETags (W/"…") sa objavujú častejšie, než by si chcel. Pre If-Range je však rozhodujúce silné ETag-zladenie. V praxi to znamená: ak dostaneš len weak ETag, resume je menej spoľahlivé. Môžeš ho naďalej použiť, ale mal by si byť konzervatívnejší (napr. pri najmenších nezrovnalostiach začať sťahovanie od začiatku) a pri obzvlášť kritických súboroch navyše vyžadovať out-of-band kontrolu integrity (hash z manifestu/API), ak je k dispozícii.

Neznáma celková dĺžka (chunked) a chýbajúca plauzibilita

Ak nepríde Content-Length (napr. chunked transfer), stratíš jednoduchú kontrolu ukončenia „lokálna dĺžka == očakávaná dĺžka“. Resume môže aj tak fungovať, ale potrebuješ spoľahlivé validátory a kvalitné logovanie. Bez týchto opôr je ladenie zdĺhavé, lebo ani nevieš povedať, či ti chýbajú byty alebo ich je priveľa.

Paralelizácia: až keď je základ spoľahlivý

Načítanie viacerých range-ov paralelne je možné, ale znásobí to problémy so stavom: správa segmentov, náhodné zápisy, zlučovanie, protokolovanie chýb, retry pre každý segment. Pre mnohé procesne-blízke softvérové riešenia je to overkill. Stabilný prínos často dosiahneš už jednoduchým kombinovaním: sériového sťahovania + resume + jasnej rozhodovacej matice. Paralelizácia sa oplatí až vtedy, keď si overil stabilnú podporu Range-ov a merania jasne ukazujú, že ťa obmedzuje skutočne sieť alebo server.

Debugging, ktorý za 5 minút prinesie jasnosť

Resume-Bugs sind selten „nur ein falsches if“. Meist ist es eine Kombination aus Infrastruktur (Proxy/CDN), Server-Konfiguration, Timeouts und lokalem State. Darum lohnt sich strukturierte Protokollierung pro Versuch. Nicht als Textwüste, sondern als konsistente Felder:

  • Požiadavka: URL (bez tajných údajov), metóda, Range (začiatok/koniec), If-Range (ETag alebo dátum).
  • Odpoveď: stavový kód, Accept-Ranges, ETag, Last-Modified, Content-Length, Content-Range (surový a parsovaný).
  • Lokálny stav: Part-Pfad, dĺžka part pred požiadavkou, režim zápisu (Append/Overwrite), dĺžka po zápise.
  • Rozhodnutie: „Resume accepted“, „Resume rejected (200) – RESTart“, „416 – reset“, „Validator mismatch – RESTart“.

S týmito presne definovanými údajmi môžeš testovať hypotézy: „Ignoruje server Range?“, „Mení sa ETag medzi požiadavkami?“, „Je offset nesprávne zistený?“, „Zapisuje do súboru druhý proces?“ – bez toho, aby si musel najprv vytiahnuť Wireshark.

Kedy zámerne neimplementovať Resume

Niekedy je najčistejšie architektonické rozhodnutie zámerne vynechať Resume – nie zo zjednodušenia, ale preto, že v tvojom scenári prináša viac chýb než prínos:

  • URL bez viazania na verziu: Ak sa za rovnakou URL pravidelne nachádzajú nové obsahy, Resume bez silného validátora predstavuje riziko integrity.
  • Žiadne použiteľné validátory: Ak ani ETag, ani Last-Modified nie sú konzistentné, je „pokračovanie“ hazard. Pri kritických importoch je to často horšie než kontrolovaný reštart.
  • Stiahnutie malé a časté: Tu sú retry-stratégie (Timeout, Backoff, prípadne Mirror-URL) udržiavateľnejšie a v prevádzke jednoduchšie vysvetliť.

Pragmatické bezpečnostné siete, ktoré sa oplatia

Karanténa namiesto mazania

Ak zlyhá pokus o Resume, nevymazávaj .part reflexívne. Premenuj ho (napr. .part.bad + časová pečiatka). To sprístupní problémy so serverom/proxy, pretože neskôr môžeš porovnať Response-Headery a stav súboru. A zabráni tomu, aby zriedkavý problém pri „upratovaní“ zmizol z analýzy.

Zámky proti duplicitnému spusteniu

Ak dva procesy súčasne zapisujú do toho istého .part, všetky HTTP-validátory sú bezcenné. Exkluzívny Lock (Datei-Lock alebo systémový Mutex) nie je teda luxus, ale základná ochrana. Metadáta môžu navyše niesť informáciu o vlastníkovi (Instanz-ID), aby si mohol „opustené“ stiahnutia spoľahlivo identifikovať a upratať.

Pravidlá na rýchlu orientáciu, ktoré naozaj potrebuješ

  • Append iba pri 206 – a iba s overeným Content-Range (jednotka bytes, počiatočný offset sedí).
  • 200 znamená: reštartovať (overwrite), nikdy nepripájaj na čiastočný súbor.
  • If-Range znižuje miešanie verzií, nenahrádza však povinnosť vyhodnotiť status/Content-Range.
  • 416 je signál resetu: lokálny offset už nesedí, stav zahodiť/dať do karantény.

Záver: najprv integrita, potom optimalizácia

Ein HttpClient Download mit Resume ist dann gut, wenn er langweilig ist: Er setzt Range nur, wenn der lokale Zustand belastbar ist, er hängt nur bei 206 plus geprüftem Content-Range an, und er verhindert Versionsmix über If-Range und Validatoren. Alles andere ist ein „sieht schnell aus“-Feature, das dir später Zeit frisst, weil Fehler selten sind und dann kaum nachstellbar.

Wenn du für Updater, Importstrecken oder Services eine robuste Download-Pipeline (Resume, Logging, Partial-File-Strategie, Betriebskanten) sauber aufsetzen willst, lässt sich das oft in einem kurzen Architektur-Check klären – inklusive der Frage, ob sich die Komplexität in deinem Umfeld überhaupt lohnt. Kontakt aufnehmen und Vorhaben mit Net-Base besprechen.

Zdroje a doplňujúce informácie

Odborné kľúčové tvrdenia boli redakčne kontextualizované na základe nasledujúcich externých zdrojov.

  1. RFC 9110: HTTP Semantics (www.rfc-editor.org)
    HTTP Range-Requests sú voliteľné; servery môžu Range ignorovať a odpovedať ako pri bežnom GET, preto musí klient vyhodnotiť stavový kód a hlavičky namiesto slepého spoliehania sa na pokračovanie.
  2. HTTP range requests – HTTP | MDN (developer.mozilla.org)
    Accept-Ranges je signálom podpory Range, ale klienti musia dôsledne overiť správanie odpovede (206/Content-Range).
  3. RangeHeaderValue Class (System.Net.Http.Headers) | Microsoft Learn (learn.microsoft.com)
    .NET poskytuje s RangeHeaderValue typizovanú reprezentáciu hlavičky Range; servery nemusia podporovať Range-requests.

Prediskutovať projekt alebo modernizačný zámer s Net-Base.

ďalší krok

Keď sa z témy stane reálny projekt, architektúru, existujúci stav a prevádzku treba včas posudzovať spoločne.

Podporujeme nielen pri jednotlivých otázkach, ale aj vtedy, keď sa z fragmentov zdrojového kódu, tém súvisiacich s legacy systémami alebo nápadov na portál má stať robustný podnikový projekt.

  • Stav, cieľový obraz a technické riziká sa hodnotia spoločne.
  • REST, prístup k údajom, portály a nasadenie nebudú odložené na neskôr ako následné úlohy.
  • Včas identifikujete, ktorá cesta je ekonomicky a prevádzkovo životaschopná.

Zdieľať príspevok

Tento príspevok priamo zdieľať

LinkedIn, X, XING, Facebook, WhatsApp a e‑mail sú okamžite k dispozícii. Pre Instagram pripravíme priamo odkaz a stručný text.

E-mail

Instagram sa otvorí v novej karte. Odkaz a krátky text sa predtým skopírujú do schránky.

Napísať komentár

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert