HTTP-Bereichskopf

81

Ich habe http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35 gelesen und versucht herauszufinden, wie ein Dateidownload fortgesetzt werden kann.

Angenommen, eine Datei hat eine Länge von 100 Byte und ich habe alle 100 Bytes. Ich weiß jedoch nicht, wie groß die erwartete Dateigröße sein soll, daher frage ich nach der Datei und gebe einen Bereichskopf an, der folgendermaßen aussieht:

Range: bytes=100-

Ist dies eine gültige Bereichsanforderung?

Dhruvbird
quelle
5
Ähm, das Beispiel darunter zitiert 'Bytes = 9500-' als gültig, also ...
Wrikken
1
Die aktuellste Referenz
Mark Nottingham
2
Sie können zuerst eine HEAD-Anfrage stellen und die Dateilänge überprüfen.
Matheus Rocha

Antworten:

54

Es ist eine syntaktisch gültige Anfrage, aber keine zufriedenstellende Anfrage. Wenn Sie in diesem Abschnitt weiter schauen, sehen Sie:

Wenn eine syntaktisch gültige Byte-Range-Menge mindestens eine Byte-Range-Spezifikation enthält, deren erstes Byte-Pos kleiner als die aktuelle Länge des Entity-Body ist, oder mindestens eine Suffix-Byte-Range-Spezifikation mit einem Non - Null Suffixlänge, dann ist der Bytebereichssatz erfüllbar. Andernfalls ist der Bytebereichssatz nicht zufriedenstellend. Wenn der Bytebereichssatz nicht zufriedenstellend ist, MUSS der Server eine Antwort mit dem Status 416 zurückgeben (angeforderter Bereich nicht erfüllbar) . Andernfalls MUSS der Server eine Antwort mit dem Status 206 (Teilinhalt) zurückgeben, die die erfüllbaren Bereiche des Entitätskörpers enthält.

In Ihrem Beispiel sollte der Server also einen 416 zurückgeben, da dies kein gültiger Bytebereich für diese Datei ist.

Marc Novakowski
quelle
Gibt es also eine Möglichkeit, wie ein Client einen Download fortsetzen kann, ohne einen HEAD-Aufruf zu tätigen, um zuerst die Länge des Inhalts zu ermitteln und dann zu rechnen und den tatsächlichen Inhalt abzurufen? Ich meine eine Art offene Adressierung wie "Gib mir alle Bytes nach so und so Byte ..."
Dhruvbird
5
Der Client weiß bereits, ob er alle Daten aus der ursprünglichen Anforderung hat - er sollte entweder einen Content-Length-Header in der ursprünglichen Antwort erhalten haben, oder wenn er eine Chunk-Codierung erhalten hat, hat er einen Chunk mit der Länge Null erhalten, um die anzugeben Antwort war vollständig. Wenn Sie diesen Status nicht gespeichert haben und nur einen Teil der Bytes auf der Festplatte haben, müssen Sie entweder eine HEAD-Anforderung ausführen oder den Range-Header verwenden, um nach einem Byte-Bereich zu fragen, und wenn Sie einen 416 zurückerhalten Antwort Sie wissen, dass Sie alle Bytes haben.
Marc Novakowski
Ich denke, mit Expect-Continue können Sie mehr oder weniger wie gewünscht Streaming-Chunks erstellen.
MJB
@MarcNovakowski Betrachten Sie den Fall von wget und verwenden Sie das Flag -c. Da wget keine Metadaten über die vollständige Datei verwaltet, wird angenommen, dass die Größe der Datei auf der Festplatte 99 Byte beträgt. wget fordert den Bytebereich "100-" an, und ich bin der Meinung, dass der Server mit einer Antwort von 0 antworten sollte, da die Anforderung nur 1 nach dem Ende der Datei liegt.
Dhruvbird
147

Wie Wrikken vorgeschlagen hat, handelt es sich um eine gültige Anfrage. Es kommt auch häufig vor, dass der Client Medien anfordert oder den Download fortsetzt.

Ein Client testet häufig, ob der Server andere Fernkampfanforderungen verarbeitet als nur nach einer Accept-RangesAntwort zu suchen . Chrome sendet immer eine Range: bytes=0-erste GET-Anfrage für ein Video, sodass Sie diese nicht ablehnen können.

Immer wenn ein Client Range:in seine Anfrage einschließt , selbst wenn er fehlerhaft ist, erwartet er eine teilweise inhaltliche Antwort (206). Wenn Sie während der HTML5-Videowiedergabe nach vorne suchen, fordert der Browser nur den Startpunkt an. Zum Beispiel:

Range: bytes=3744-

Damit der Client Videos ordnungsgemäß abspielen kann, muss Ihr Server in der Lage sein, diese unvollständigen Bereichsanforderungen zu verarbeiten.

Sie können den in Ihrer Frage angegebenen Typ des Bereichs auf zwei Arten behandeln:

Zuerst können Sie mit dem in der Antwort angegebenen angeforderten Startpunkt antworten, dann mit der Gesamtlänge der Datei minus eins (der angeforderte Bytebereich ist nullindiziert). Zum Beispiel:

Anfrage:

GET /BigBuckBunny_320x180.mp4 
Range: bytes=100-

Antwort:

206 Partial Content
Content-Type: video/mp4
Content-Length: 64656927
Accept-Ranges: bytes
Content-Range: bytes 100-64656926/64656927

Zweitens könnten Sie mit dem in der Anfrage angegebenen Ausgangspunkt und einer offenen Dateilänge (Größe) antworten. Dies gilt für Webcasts oder andere Medien, bei denen die Gesamtlänge unbekannt ist. Zum Beispiel:

Anfrage:

GET /BigBuckBunny_320x180.mp4
Range: bytes=100-

Antwort:

206 Partial Content
Content-Type: video/mp4
Content-Length: 64656927
Accept-Ranges: bytes
Content-Range: bytes 100-64656926/*

Tipps:

Sie müssen immer mit der im Bereich enthaltenen Inhaltslänge antworten. Wenn der Bereich mit Anfang bis Ende vollständig ist, ist die Inhaltslänge einfach der Unterschied:

Anfrage: Bereich: Bytes = 500-1000

Antwort: Inhaltsbereich: Bytes 500-1000 / 123456

Denken Sie daran, dass der Bereich auf Null indiziert ist und daher Range: bytes=0-999tatsächlich 1000 Bytes und nicht 999 Bytes anfordert. Antworten Sie also mit etwas wie:

Content-Length: 1000
Content-Range: bytes 0-999/123456

Oder:

Content-Length: 1000
Content-Range: bytes 0-999/*

Vermeiden Sie jedoch nach Möglichkeit die letztere Methode, da einige Mediaplayer versuchen, die Dauer anhand der Dateigröße zu ermitteln. Wenn Ihre Anfrage Medieninhalte betrifft, was meine Vermutung ist, sollten Sie deren Dauer in die Antwort aufnehmen. Dies erfolgt im folgenden Format:

X-Content-Duration: 63.23 

Dies muss ein Gleitkomma sein. Im Gegensatz dazu Content-Lengthmuss dieser Wert nicht genau sein. Es wird verwendet, um dem Player beim Suchen im Video zu helfen. Wenn Sie einen Webcast streamen und nur eine allgemeine Vorstellung davon haben, wie lange er dauern wird, ist es besser, Ihre geschätzte Dauer anzugeben, als sie insgesamt zu ignorieren. Für einen zweistündigen Webcast können Sie also Folgendes hinzufügen:

X-Content-Duration: 7200.00 

Bei einigen Medientypen, z. B. webm, müssen Sie auch den Inhaltstyp angeben, z.

Content-Type: video/webm 

All dies ist erforderlich, damit die Medien ordnungsgemäß abgespielt werden können, insbesondere in HTML5. Wenn Sie keine Dauer angeben, versucht der Player möglicherweise, die Dauer (um das Suchen zu ermöglichen) anhand der Dateigröße zu ermitteln. Dies ist jedoch nicht korrekt. Dies ist in Ordnung und für Webcasts oder Live-Streaming erforderlich, jedoch nicht ideal für die Wiedergabe von Videodateien. Sie können die Dauer mit Software wie FFMPEG extrahieren und in einer Datenbank oder sogar im Dateinamen speichern.

X-Content-Durationwird zugunsten von auslaufen Content-Duration, also würde ich das auch einschließen. Eine grundlegende Antwort auf eine "0-" Anfrage würde mindestens Folgendes umfassen:

HTTP/1.1 206 Partial Content
Date: Sun, 08 May 2013 06:37:54 GMT
Server: Apache/2.0.52 (Red Hat)
Accept-Ranges: bytes
Content-Length: 3980
Content-Range: bytes 0-3979/3980
Content-Type: video/webm
X-Content-Duration: 2054.53
Content-Duration: 2054.53

Noch ein Punkt: Chrome startet seine erste Videoanfrage immer mit folgendem:

Range: bytes=0-

Einige Server senden eine reguläre 200-Antwort als Antwort, die sie akzeptiert (jedoch mit eingeschränkten Wiedergabeoptionen). Versuchen Sie jedoch, stattdessen eine 206 zu senden, um anzuzeigen, dass Ihr Server Bereiche verarbeitet. Laut RFC 2616 ist es akzeptabel, Bereichskopfzeilen zu ignorieren.

Victor Stoddard
quelle
Was tun Sie, wenn der Inhalt ein Live-Videostream ist, der keine feste Dauer hat?
Joel Barsotti
@ Joel, du musst mit einer Dauer antworten, auch wenn du es nicht weißt. In diesem Fall versuchen Sie einfach 0.0. Für den Client spielt die Dauer sowieso keine Rolle, da Sie normalerweise keinen Live-Stream scannen können. Wenn die 0.0 nicht funktioniert, probieren Sie einfach etwas wirklich Hoches wie 1000000.00 aus.
Victor Stoddard
@ VictorStoddard Kann Chunked Streaming auf den regulären Dateidownload angewendet werden, bei dem in der Clientanforderung kein Range-Header vorhanden ist? Wie soll der Server in diesem Fall reagieren?
Gkiko
@gkiko Es gibt keinen großen Unterschied außer der Verwendung des Transfer-Encoding-Headers anstelle der Inhaltslänge bei der Chunked Transfer Encoding. Chunks können aus einer einzelnen Datei stammen und der Server kann die Chunk-Größe festlegen. Der Client sollte die Chunks puffern und zusammenfügen, sobald sie empfangen werden. Alternativ verwendet HTTP-Streaming voraufgezeichnete Segmente einer Mediendatei, in denen sie als einzelne Teile (ts-Dateien) auf dem Server gespeichert werden. Diese Segmente werden mithilfe regulärer HTTP-Datei-GET-Anforderungen bedient, die aus einer Indexdatei abgerufen werden. Ich habe das Segmentieren als schwierig empfunden, aber das war vor Jahren.
Victor Stoddard
Inhaltslänge: 64656927 Akzeptanzbereiche: Bytes Inhaltsbereich: Bytes 100-64656926 Warum ist die Inhaltslänge nicht '64656827'?
Wind
7

Im Gegensatz zu Mark Novakowskis Antwort, die aus irgendeinem Grund von vielen positiv bewertet wurde, handelt es sich um eine gültige und zufriedenstellende Anfrage.

Tatsächlich ist der Standard, wie Wrikken betonte, ein solches Beispiel. In der Praxis reagiert Firefox erwartungsgemäß auf solche Anfragen (mit einem 206-Code), und genau das verwende ich, um den progressiven Download zu implementieren, dh nur den Schwanz einer langen Protokolldatei zu erhalten, die beim Abrufen in Echtzeit wächst.

Francesco Potortì
quelle
2
Bitte lesen Sie die Antwort von Marc Novakowki noch einmal. "befriedigend" hat eine besondere Bedeutung in der RFC, die er zitierte. Diese Anforderung ist nicht erfüllbar, da die angeforderten Bytes die Länge der Datei überschreiten.
Scott Lamb
1
Firefox ist nicht das Softwareelement, das auf die Anfrage reagiert, es ist ein http-Server
Colin D
Ja, sorry, ich meinte Apache
Francesco Potortì
5

Für Leute, die 2019 über Victor Stoddards Antwort stolpern und hoffnungsvoll werden und Augen haben, beachten Sie Folgendes:

a) Die Unterstützung für die Dauer von X-Inhalten wurde in Firefox 41 entfernt: https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/41#HTTP

b) Ich denke, es wurde nur in Firefox für .ogg Audio und .ogv Video unterstützt, nicht für andere Typen.

c) Ich kann nicht sehen, dass es jemals in Chrome unterstützt wurde, aber das kann nur ein Mangel an Forschung meinerseits sein. Aber seine Anwesenheit oder Abwesenheit scheint auf die eine oder andere Weise keine Auswirkungen auf Web- oder Ogv-Videos zu haben, wie dies heute in Chrome 71 der Fall ist.

d) Ich kann nirgendwo finden, wo 'Content-Duration' 'X-Content-Duration' für irgendetwas ersetzt hat. Ich glaube nicht, dass 'X-Content-Duration' lange genug gelebt hat, um einen Nachfolge-Headernamen zu haben.

Ich denke, dies bedeutet, dass Sie ab heute WebM- oder OGV-Container, die Streams enthalten, deren Dauer nicht bekannt ist (z. B. die Ausgabe einer ffpeg-Pipe), an Chrome oder FF senden möchten und in denen sie scrubbbar sein sollen Als HTML 5-Videoelement haben Sie wahrscheinlich kein Glück. Firefox 64.0 unternimmt einen halbherzigen Versuch, diese abwischbar zu machen, unabhängig davon, ob Sie über Bereichsanforderungen dienen oder nicht. Es wird jedoch verwirrt und wirft ein sich drehendes Rad auf, bis der Stream vollständig heruntergeladen ist, wenn Sie ein paar Mal mehr suchen, als es für angemessen hält. Chrome versucht es nicht einmal, es macht nur Spaß und lässt Sie überhaupt nicht schrubben, bis der gesamte Stream abgespielt ist .

Chris McDonough
quelle
Hier ist ein langer Thread der FF-Entwickler, der über die Unterstützung dieser Art von Dateien spricht. bugzilla.mozilla.org/show_bug.cgi?id=657791
Chris McDonough