Net-Base Magasin

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. Refaktorering. Migrasjon.

Modernisere vaksen programvare utan å miste den faglege substansen i systemet.

Eldre system Delphi Refaktorisering Idriftsetjing

Oppretthald faglogikk

Verdifull forretningslogikk held fram med å vere synleg og vert ikkje gravd ned under ei total omskriving.

Avkople kodebasen

Skil ansvarsområda, slik at nye grensesnitt og nye tenester kan koblast til kontrollert.

Gjer migrasjonen planleggbar.

Levere, teste og setje i drift trinnvis i staden for å risikere drifta med ein abrupt overgang.

24.10.2026

Frå magasinetema til prosjektpraksis

Passande teneste- og tekniske sider til innlegget

Scena er ubehageleg, men typisk: Ein nattleg jobb lastar ned eit stort arkiv, framdriftslinja står på 93 %. Så ein kort VPN-rykk, timeout, avbrot. Morgonen etter ligg ei fil i målkatalogen, storleik «passar», men ved utpakking kjem berre «unexpected end of file». Den som då reflexmessig ettermonterer «Resume», endar fort i neste kanttilfelle: Nedlastinga kjører ferdig, men er sett saman av to versjonar.

Nettopp til dette er ein HttpClient Download mit Resume meint – men berre viss du handsamar han som ein liten datapipeline: med klare reglar for når du kan leggje til, korleis du sjekkar versjonstrekk (ETag/If-Range) og korleis du administrerer lokale mellomtilstandar (Partial Files) slik at drift og debugging ikkje blir eit gjetteleikarspel.

Målkonflikten er tydeleg: spare bandbreidd og tid (fortsetje i staden for å starte på nytt) står mot integritet og vedlikehaldseigenskapar (inga stille versjonsblanding, etterreknelege avgjersler, reproducerbart feilmønster). Denne artikkelen er bygd som eit avvegingkart: nytte, innsats, risiko, avgrensingar – og ei pragmatisk linje for når du verkeleg bør implementere gjenopptaking og når eit godt retry er betre.

Avvegingkart: To mål, ein hard avveging

Gjenopptaking er ikkje verdt å gjere berre fordi det er «moderne», men fordi miljøet ditt krev det: store filer, ustabile samband, tidsvindauge i batch-jobbar, brukarar som avbryt nedlastingar, eller tenester som skal fortsetje etter omstart der dei slapp. Samstundes er gjenopptaking staden der ein lett får innført datakorrosjon – og det blir ofte oppdaga først langt seinare (import feilar, signatur ugyldig, setup kan ikkje køyrast).

Situasjon Nytte av gjenopptaking Typisk risiko Plausibel avgjerd
Store filer over WAN/VPN eller mobilnett høg (tid, bandbreidd) «200 i staden for 206» blir blindt lagt til Gjenopptaking ja, men defensivt (Status/Content-Range/Validator)
«latest»-URLar / artefaktar utan versjonsbinding mellom Versjonsblanding etter mellomoppdatering Gjenopptaking berre med sterk validator, elles ny nedlasting
Internt lagringssystem med stabile ETags høg låg, om headerane er konsistente Gjenopptaking svært godt egna, persistér metadata
Små filer (nokre få dusin MB) låg Kompleksiteten veg tyngre Full nedlasting + Retry/Backoff er som regel betre
Skiftande proxyar/CDN-ar, uklår header-situasjon nytten uklar Inkonsistente validatorar, vanskeleg debugging Berre med god loggføring; elles medvite konservativ

Kvarfor Resume er meir enn «å setje Range»

HTTP kjenner til byte-range-requests gjennom Range-headeren. Den viktige, ofte oversette observasjonen: Støtte for Range er valfri. Ein server kan ignorere Range og svare som om det var ein vanleg GET – altså med 200 OK og full body. For klientar betyr dette: Sanninga står i responsen, ikkje i intensjonen i førespurnaden din.[Kjelde]

Ein Resume-klient som forvekslar «Range sendt» med «ein del av innhaldet kjem», gjer nett den feilen du såg tidleg i scena: Ein full nedlasting blir lagt til ei allereie delvis lasta fil. Resultat: fila verkar stor nok, men er logisk øydelagt.

Suksessignal: 206 pluss plausibelt Content-Range

Når Resume verkeleg fungerer, ser du typisk:

  • Status 206 Partial Content i staden for 200 OK
  • Content-Range med Range-eining bytes og det forventa start-offsetet
  • Konsistente validatorar (t.d. ETag), om du brukar dei for versjonssamsvar

Desse kontrollane er ikkje valfrie. RFC 9110 formulerer eksplisitt at mottakarar ikkje får setje saman ei 206-respons med ukjend range-unit med ei lagra repræsentasjon. Praktisk følgje: Uten korrekt forstått Content-Range kan ein ikkje legge til data.[Kjelde]

If-Range og ETag: Den ryddige vegen mot versjonsblanding

Kjernespørsmålet ved gjenopptaking er ikkje teknikken for å leggje til data, men om du framleis lastar same ressurs. HTTP leverer validatorar for dette. ETag (Entity Tag) er det mest presise verkemiddelet: ein serverside-identifikator for ei konkret repræsentasjon. Last-Modified er eit tidsstempel, ofte nyttig, men mindre skjerpt (tidsoppløysing, repakking, ulike mellomsteg via CDN-ar).

Mechanismen for «Range berre når det verkeleg er same versjon» heiter If-Range: Du sender Range pluss ein betingelse. Dersom validatoren passar, svarer serveren med Range; passar han ikkje, skal serveren sende heile ressursen. For ETag-godkjenning gjeld at betinga berre er oppfylt ved sterk ETag-samsvar (strong comparison). Dette er ikkje tolking – det står klart i standarden.[Kjelde]

Driftsrealiteten er framleis: Dersom serveren ikkje støttar Range, må han ignorere If-Range. Det høyrest akademisk ut, men er ein direkte krav til klienten din: Du må også rekne med 200 OK og ta ei klart definert avgjerd (starte på nytt, ikkje leggje til).[Kjelde]

Partial-File-Strategi: Det lokale motstykket til HTTP-semantikken

Resume betyr at du forvaltar lokal tilstand. Og denne tilstanden må vere stabil også etter krasj, omstart, parallell oppstart eller brukarhandlingar («klikk på nytt»). Ein ryddig Partial-File-strategi består av tre artefaktar:

  • Partial-fil som einaste skrivepunkt under nedlasting (t.d. foo.zip.part)
  • Metadatane lagra separat (t.d. foo.zip.part.meta.json): URL, tidspunkt, ETag, Last-Modified, forventa lengd (hvis kjent), eventuelt Content-Type, samt den lokale lengda ved siste rene Flush
  • Atomar avslutning: først etter fullstendig nedlasting og plausibilitetssjekk blir fila omdøpt/erstatta med det endelege namnet

Kvifor metadatane? Fordi utan dei kjenner du ved neste oppstart berre filstorleiken – men ikkje om ho høyrer til nettopp denne server-versjonen. Nøyaktig denne informasjonen avgjer om du kan fortsetje.

HttpClient-nedlasting med Resume: beslutningslogikk som du kan teste

System.Net.HttpClient gir deg forespørslar, svar og straumar. Robustnaden kjem frå beslutningsmatrisa di. Dersom du formulerer ho tydeleg, kan du òg teste han tydeleg (inkludert „Server ignoriert Range“, „Validator mismatch“, „416“).

Minimal prosess i 6 steg

  1. Fastslå lokal tilstand: Finnst .part? Les lengda som offset. Last inn metadatane.
  2. Avgjer resume-moglegheit: Prøv berre Resume dersom offset > 0 og minst éin brukbar validator finst i metadatane (ETag føretrekt).
  3. Bygg forespørsel: Set Range frå offset. Set i tillegg If-Range (ETag, elles Last-Modified).
  4. Klassifiser svar:
    • 206: parse Content-Range, verifiser Range-Unit bytes, start-offset må stemme eksakt. Berre då append/legg til.
    • 200: aldri legg til. Forkast eller sett delvis tilstand i karantene og last ned på nytt.
    • 416 Range Not Satisfiable: offset stemmer ikkje lenger (lokalt eller på serversida). Nullstill tilstand og start på nytt.
    • anna: feilhandtering med tydeleg diagnose (logg statuskode + header).
  5. Skriv straumen: i blokkar, flush regelmessig, oppdater metadatane (t.d. «siste Flush ved lengd X»).
  6. Avslutning: storleiks-/plausibilitetssjekk (dersom mogleg). Deretter atomisk omdøyping/erstatning.

RangeHeaderValue: ikkje eit detalj, men feilførebygging

.NET tilbyr for Range-headeren den typiserte klassen RangeHeaderValue. Det er ikkje berre «pen kode»: det reduserer syntaktiske headerfeil og gjer loggar enklare å tolke, fordi du produserer konsistente verdiar. Microsoft dokumenterer òg den driftsrelevante kanten: serverar treng ikkje støtte Range-forespørslar; Accept-Ranges er eit hint, ikkje ei garanti.[Quelle]

Konstruert døme: Når „200 OK“ øydelegg Resume-et ditt

Typiske fallgruver og korleis du ser dei i loggen

Accept-Ranges: nyttig signal, men ingen garanti

Mange team brukar Accept-Ranges som «resume kan serveren». MDN skildrar headeren nett slik som ein støtteindikator («alt bortsett frå none»). Det er praktisk for diagnose og telemetri. Likevel: avgjer aldri berre på grunnlag av han. Ein proxy, eit auth-gateway eller eit CDN kan endre åtferda. Logikken din må derfor alltid basere seg på statuskoden (206 vs. 200) og på Content-Range, ikkje på håpet i Accept-Ranges.[Kjelde]

Weak ETags (W/…) og transformasjonar

Weak ETags (W/"…") dukkar opp oftare enn ein skulle ønske. For If-Range er derimot eit sterkt ETag-match avgjerande. I praksis betyr det: om du berre får weak ETag, er resume mindre robust. Du kan framleis bruke det, men du bør vere meir konservativ (t.d. starte på nytt ved minste avvik) og for særskild kritiske filer supplere med ein out-of-band integritetskontroll (hash frå manifest/API), om tilgjengeleg.

Ukjend totalstorleik (chunked) og manglande plausibilitet

Når det ikkje kjem Content-Length (t.d. chunked transfer), mistar du den enkle sluttkontrollen «lokal lengd == forventa lengd». Resume kan framleis fungere, men du treng solide validatorar og god logging. Uten desse støttane vert feilsøking knotete, fordi du ikkje engong kan seie om det manglar byte eller om det er for mykje.

Parallelisering: først når grunnlaget er på plass

Å laste fleire ranges parallelt er mogleg, men det gjev ein eksplosjon i tilstandsproblem: segmenthandtering, random-write, merging, feillogg, retry per segment. For mange prosessnære løysingar er dette overkill. Den stabile gevinsten kjem ofte allereie frå: seriell nedlasting + resume + ei klar avgjerdsmatrise. Parallelisering løner seg først når du har dokumentert stabil range-støtte og konkrete målingar viser at du faktisk sit på eit nettverks- eller serverlimit.

Debugging som gir klarheit på 5 minutt

Resume-feil er sjeldan «berre ein feil i eit if-uttrykk». Som regel er det ei kombinasjon av infrastruktur (Proxy/CDN), serverkonfigurasjon, timeoutar og lokal tilstand. Difor løner det seg med strukturert loggføring per forsøk. Ikkje som ei tekstørken, men som konsistente felt:

  • Request: URL (utan sensitive opplysningar), metode, Range (start/slutt), If-Range (ETag eller dato).
  • Response: Statuskode, Accept-Ranges, ETag, Last-Modified, Content-Length, Content-Range (rå og parsa).
  • Lokal tilstand: part-veg, part-lengd før førespurnad, skrivemodus (Append/Overwrite), lengd etter skriving.
  • Avgjerd: «Resume accepted», «Resume rejected (200) – RESTart», «416 – reset», «Validator mismatch – RESTart».

Med nettopp desse data kan du teste hypotesar: «Ignorerer serveren Range?», «Endrar ETag seg mellom førespurnadar?», «Er offset feil utreda?», «Skriv ein andre prosess samtidig?» – utan at du treng å hente fram Wireshark først.

Når du medvite ikkje implementerer Resume

Den reinaste arkitekturavgjerda er av og til å utelate Resume – ikkje av latskap, men fordi det i ditt scenario gir større feilflate enn nytte:

  • URL utan versjonsbinding: Dersom same URL regelmessig peikar på nytt innhald, er Resume utan ein sterk validator eit integritetsrisiko.
  • Ingen brukbare validatorar: Når verken ETag eller Last-Modified er konsistente, er «fortsetjing» eit veddemål. Ved kritiske importar er det ofte dårlegare enn ein kontrollert omstart.
  • Liten og hyppig nedlasting: Her er retry-strategiar (timeout, backoff, eventuelt mirror-URL) meir vedlikehaldsvende og enklare å forklare i drift.

Pragmatiske tryggingsnett som lønner seg

Karantene framfor sletting

Dersom eit Resume-forsøk feilar, slett .part ikkje reflexivt. Omnamn det (t.d. .part.bad + tidsstempel). Det gjer server-/proxy-problem synlege, fordi du seinare kan sette Response-header mot filtilstand. Og det hindrar at eit sjeldan problem forsvinn frå analysen ved «opprydding».

Låsing mot dobbeltstartar

Når to prosessar samtidig skriv same .part, er alle HTTP-validatorar verdilause. Ein eksklusiv lås (fil-lås eller systemomfattande mutex) er difor ikkje luksus, men grunnvern. Metadatane kan i tillegg innehalde ein owner-informasjon (instans-ID), så du lett kan identifisere og rydde opp i «forlatne» nedlastingar.

Hurtigreglar du verkeleg treng

  • Append berre ved 206 – og berre med verifisert Content-Range (Unit bytes, start-offset passar).
  • 200 betyr: start på nytt (overwrite), aldri legg til ei partial-fil.
  • If-Range reduserer versjonsblanding, men erstattar ikkje plikta til å evaluere status/Content-Range.
  • 416 er eit reset-signal: lokal offset stemmer ikkje lenger, forkast/karantener tilstanden.

Avslutning: Fyrst integritet, så optimalisering

Ein HttpClient-nedlasting med gjenopptaking er god når han er kjedeleg: Han set Range berre når den lokale tilstanden er påliteleg, han heng berre ved 206 pluss verifisert Content-Range, og han forhindrar versjonsblanding over If-Range og validatorar. Alt anna er ein „ser raskt ut“-funksjon som et opp tid seinare, fordi feil er sjeldne og då nesten umogleg å etterprøve.

Om du for oppdaterarar, importløp eller tenester vil setje opp ei robust nedlastings-pipeline (gjenopptaking, logging, delfil-strategi, driftskantar) på ein ryddig måte, kan dette ofte klarleggast i ein kort arkitektur-sjekk – inkludert spørsmålet om kompleksiteten i ditt miljø i det heile tatt er verdt det. Ta kontakt og diskuter prosjektet med Net-Base.

Kjelder og vidareførande informasjon

De faglege kjerneutsaga er redaksjonelt sette i kontekst med følgjande eksterne kjelder.

  1. RFC 9110: HTTP Semantics (www.rfc-editor.org)
    HTTP Range-Requests er valfrie; serverar kan ignorere Range og svare som ved ein vanleg GET, derfor må klienten vurdere statuskoden og headerane i staden for å blint leggje til i fila.
  2. HTTP range requests – HTTP | MDN (developer.mozilla.org)
    Accept-Ranges er eit signal om Range-støtte, men klientar må robust kontrollere responsatferda (206/Content-Range).
  3. RangeHeaderValue Class (System.Net.Http.Headers) | Microsoft Learn (learn.microsoft.com)
    .NET gir med RangeHeaderValue ei typa representasjon av Range-headeren; serverar treng ikkje støtte Range-Requests.

Diskuter prosjekt eller moderniseringsprosjekt med Net-Base.

neste steg

Når temaet blir eit reelt prosjekt, bør arkitektur, eksisterande system og drift tidleg saman vurderast.

Vi støttar ikkje berre ved enkeltspørsmål, men òg når korte kildekodesnuttar, legacy-tema eller portalidéar skal utviklast til eit robust bedriftsprosjekt.

  • Eksisterande tilstand, målbiletet og tekniske risikoar blir vurderast samla.
  • REST, datatilgang, portalar og utrulling blir ikkje utsett til seinare fasar.
  • De ser tidleg kva veg som er økonomisk og driftsmessig berekraftig.

Del innlegg

Del dette innlegget direkte

LinkedIn, X, XING, Facebook, WhatsApp og e-post er straks tilgjengelege. For Instagram klargjer vi lenke og kort tekst med det same.

E-post

Instagram opnar i ein ny fane. Lenkje og kort tekst blir kopiert til utklippstavla på førehand.

Skriv kommentar

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