Заголовок діапазону HTTP


81

Я читав http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35 і намагався зрозуміти, як продовжити завантаження файлу.

Наприклад, припустимо, що файл має довжину 100 байт, і я маю всі 100 байт. Однак я не знаю, яким повинен бути очікуваний розмір файлу, тому я запитую файл і вказую заголовок Range, який виглядає так:

Range: bytes=100-

Це дійсний запит на діапазон?


5
Е-м-м, приклад під ним посилається на 'bytes = 9500-' як дійсний, тому ....
Wrikken

1
Найновішим посиланням є RFC7233 - httpwg.github.io/specs/rfc7233.html
Марк Ноттінгем,

2
Ви можете спочатку зробити запит HEAD і перевірити довжину файлу.
Матеус Роша,

Відповіді:


55

Це синтаксично дійсний запит, але не задоволений запит. Якщо ви заглянете далі в цей розділ, ви побачите:

Якщо синтаксично допустимий байт-діапазон набору включає принаймні один байт-діапазон-специфікацію, перший байт-пост якого менше поточної довжини тіла-сутності, або принаймні один суфікс-байт-діапазон-специфікацію з не - нульова довжина суфікса, тоді набір діапазону байт можна задовольнити. В іншому випадку набір діапазону байтів є незадовільним. Якщо набір діапазону байтів є незадовільним, сервер ПОВИНЕН повернути відповідь зі статусом 416 (Запитаний діапазон не задовольняє) . В іншому випадку сервер ПОВИНЕН повернути відповідь із статусом 206 (Частковий вміст), що містить задовільні діапазони тіла-сутності.

Тому я думаю, що у вашому прикладі сервер повинен повернути 416, оскільки це не дійсний діапазон байт для цього файлу.


Тож чи є спосіб, яким клієнт може відновити завантаження, не зробивши виклику HEAD, щоб спочатку з’ясувати довжину вмісту, а потім здійснити обчислення та отримати фактичний вміст? Я маю на увазі якусь відкриту адресу, на кшталт "дай мені всі байти після такого-то байту ..."
dhruvbird

5
Клієнт вже знатиме, чи має всі дані з оригінального запиту - він повинен був отримати заголовок Content-Length у вихідній відповіді, або якщо він був фрагментованим кодуванням, то отримав би фрагмент нульової довжини для позначення відповідь була повною. Якщо ви не заощадили цей стан і просто маєте на байті шматок байтів, тоді так, вам доведеться або зробити запит HEAD, або скористатися заголовком Range, щоб попросити діапазон байт, і якщо ви повернете 416 відповідь ви знаєте, що у вас є всі байти.
Марк Новаковський

Думаю, Expect-Continue дозволяє робити потокові фрагменти більш-менш бажаними?
MJB

@MarcNovakowski Насправді, розглянемо випадок wget та використання прапора -c. Оскільки wget не зберігає жодних метаданих про повний файл, припустимо, розмір файлу на диску становить 99 байт. wget запитає діапазон байтів "100-", і я вважаю, що сервер повинен відповісти відповіддю 0 довжини, оскільки запит лише 1 після кінця файлу.
dhruvbird

148

Як Рріккен запропонував , це дійсний запит. Це також досить поширене явище, коли клієнт вимагає мультимедіа або відновлює завантаження.

Клієнт часто перевіряє, чи не обробляє сервер запити з дальніми даними, крім простого пошуку Accept-Rangesвідповіді. Chrome завжди надсилає Range: bytes=0-перший із запитом GET на відео, тож це неможливо відхилити.

Щоразу, коли клієнт включає Range:у свій запит, навіть якщо він неправильно сформований, він очікує часткової відповіді вмісту (206). Коли ви шукаєте вперед під час відтворення відео HTML5, браузер запитує лише початкову точку. Наприклад:

Range: bytes=3744-

Отже, для того, щоб клієнт відтворював відео належним чином, ваш сервер повинен мати можливість обробляти ці неповні запити діапазону.

Ви можете обробляти тип "діапазону", який ви вказали у своєму запитанні, двома способами:

По-перше, ви можете відповісти із запитаною початковою точкою, вказаною у відповіді, а потім загальною довжиною файлу мінус одиниця (запитуваний діапазон байтів індексується нулем). Наприклад:

Запит:

GET /BigBuckBunny_320x180.mp4 
Range: bytes=100-

Відповідь:

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

По-друге, ви можете відповісти з початковою точкою, вказаною у запиті, та відкритою довжиною (розміром) файлу. Це для веб-трансляцій та інших засобів масової інформації, загальна тривалість яких невідома. Наприклад:

Запит:

GET /BigBuckBunny_320x180.mp4
Range: bytes=100-

Відповідь:

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

Поради:

Ви завжди повинні відповідати довжиною вмісту, включеною в діапазон. Якщо діапазон заповнений, починаючи з кінця і закінчуючи, тоді довжина вмісту - це просто різниця:

Запит: Діапазон: байти = 500-1000

Відповідь: Діапазон вмісту: байти 500-1000 / 123456

Пам'ятайте, що діапазон нульово індексується, тому Range: bytes=0-999насправді вимагає 1000 байт, а не 999, тому відповідайте щось на зразок:

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

Або:

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

Але уникайте останнього методу, якщо це можливо, оскільки деякі медіаплеєри намагаються визначити тривалість із розміру файлу. Якщо ваш запит стосується медіа-вмісту, що є моїм уявленням, тоді ви повинні включити його тривалість у відповідь. Це робиться в наступному форматі:

X-Content-Duration: 63.23 

Це має бути плаваюча точка. На відміну від Content-Lengthцього, це значення не повинно бути точним. Він використовується, щоб допомогти програвачу шукати відео. Якщо ви транслюєте веб-трансляцію і маєте загальне уявлення про те, скільки часу це триватиме, краще включати свою приблизну тривалість, а не ігнорувати її взагалі. Отже, для двогодинної веб-трансляції ви можете включити щось на зразок:

X-Content-Duration: 7200.00 

У деяких типах носіїв, таких як webm, ви також повинні включити тип вмісту, наприклад:

Content-Type: video/webm 

Все це необхідне для належної відтворення засобів масової інформації, особливо в HTML5. Якщо ви не вказали тривалість, гравець може спробувати визначити тривалість (щоб дозволити пошук) з його розміру файлу, але це не буде точно. Це чудово і потрібно для веб-трансляцій або трансляцій у прямому ефірі, але не ідеально для відтворення відеофайлів. Ви можете витягти тривалість за допомогою програмного забезпечення, такого як FFMPEG, і зберегти його в базі даних або навіть у назві файлу.

X-Content-Durationвідміняється на користь Content-Duration, тому я б включив і це. Базова відповідь на запит "0-" включатиме щонайменше таке:

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

Ще один момент: Chrome завжди починає свій перший запит відео з наступним:

Range: bytes=0-

Деякі сервери надсилають у відповідь звичайну відповідь 200, яку він приймає (але з обмеженими можливостями відтворення), але спробуйте надіслати натомість 206, щоб показати, ніж ваш сервер обробляє діапазони. RFC 2616 каже, що прийнятно ігнорувати заголовки діапазонів.


Що робити, якщо вміст - це прямий відеопотік, який не має фіксованої тривалості?
Джоел Барсотті

@ Джоел, вам потрібно відповісти із тривалістю, навіть якщо ви цього не знаєте. У цьому випадку просто спробуйте 0.0. Для клієнта тривалість і так не має значення, оскільки зазвичай ви не можете сканувати прямий ефір. Якщо 0.0 не працює, просто спробуйте щось дійсно високе, наприклад 1000000.00.
Віктор Стоддард,

@VictorStoddard може застосовувати фрагментовану потокову передачу до звичайного завантаження файлу, коли в запиті клієнта немає заголовка Range? Як повинен відповісти сервер у такому випадку?
gkiko

@gkiko Немає особливої ​​різниці, крім використання заголовка Transfer-Encoding замість Content-Length у Chunked Transfer Encoding. Шматки можуть надходити з одного файлу, а сервер може встановлювати розмір шматка. Клієнт повинен буферувати та складати шматки у міру їх отримання. Крім того, HTTP Streaming використовує попередньо записані сегменти мультимедійного файлу, де вони зберігаються на сервері як окремі частини (файли ts). Ці сегменти обслуговуються за допомогою звичайних запитів GET файлу HTTP, отриманих із файлу індексу. Я виявив, що сегментування є складним, але це було багато років тому.
Віктор Стоддард

Content-Length: 64656927 Accept-Ranges: bytes Content-Range: bytes 100-64656926 Чому Content-Length не '64656827'?
iwind

7

На відміну від відповіді Марка Новаковського, яку з якихось причин багато хто підтримав, так, це обґрунтоване та задоволене прохання.

Насправді стандарт, як зазначив Вріккен, наводить саме такий приклад. На практиці Firefox відповідає на такі запити, як очікувалося (з кодом 206), і саме це я використовую для реалізації прогресивного завантаження, тобто отримую лише хвіст довгого файлу журналу, який зростає в реальному часі з опитуванням.


2
Будь ласка, прочитайте відповідь Марка Новаковкі ще раз. "задовільний" має особливе значення в RFC, який він цитував. Цей запит не можна задовольнити, оскільки запитувані байти перевищують довжину файлу.
Скотт Лемб

1
Firefox - це не програмний елемент, який відповідає на запит, це http-сервер
Колін Д,

Так, вибачте, я мав на увазі апача
Франческо Поторті

5

Для людей, які натрапляють на відповідь Віктора Стоддарда вище у 2019 році і стають сповненими надії та очей, зверніть увагу, що:

а) Підтримка X-Content-Duration була вилучена у Firefox 41: https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/41#HTTP

б) Я думаю, що у Firefox він підтримувався лише для аудіо .ogg та .ogv відео, а не для будь-яких інших типів.

в) Я не бачу, щоб це коли-небудь взагалі підтримувалось у Chrome, але це може бути просто відсутність досліджень з мого боку. Але його наявність чи відсутність, здається, не впливає так чи інакше на webm або ogv відео станом на сьогодні в Chrome 71.

г) Я не можу знайти ніде, де "Content-Duration" замінив "X-Content-Duration" на що-небудь, я не думаю, що "X-Content-Duration" прожив достатньо довго, щоб існувало ім'я заголовка наступника.

Я думаю, це означає, що на сьогоднішній день, якщо ви хочете обслуговувати контейнери webm або ogv, які містять потоки, які не знають своєї тривалості (наприклад, вихід труби ffpeg), до Chrome або FF, і ви хочете, щоб їх можна було чистити в відеоелемент HTML 5, вам, мабуть, не пощастило. Firefox 64.0 робить скрутну спробу зробити ці чисті, незалежно від того, чи обслуговуєте ви за допомогою запитів діапазону, але він заплутався і викинув крутиться до повного завантаження потоку, якщо ви шукаєте в кілька разів більше, ніж вважаєте за доцільне. Chrome навіть не намагається, він просто nopes, і не дозволить вам скраб на всіх , поки весь потік не буде закінчена гра .


Ось довга тема розробників FF, яка розповідає про підтримку таких типів файлів. bugzilla.mozilla.org/show_bug.cgi?id=657791
Кріс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.