Pierwszą rzeczą, którą musisz zrobić, jest wysłanie Accept-Ranges: bytes
nagłówka we wszystkich odpowiedziach, aby poinformować klienta, że obsługujesz częściową zawartość. Następnie, jeśli prośba o Range: bytes=x-y
odebraniu nagłówka (z x
i y
będących numerami) analizowania zakresu klient żąda, otwórz plik jako zwykły zasięgnij x
bajtów naprzód i wysłać kolejne y
- x
bajtów. Ustaw także odpowiedź na HTTP/1.0 206 Partial Content
.
Bez przetestowania czegokolwiek mogłoby to zadziałać, mniej więcej:
$filesize = filesize($file);
$offset = 0;
$length = $filesize;
if ( isset($_SERVER['HTTP_RANGE']) ) {
// if the HTTP_RANGE header is set we're dealing with partial content
$partialContent = true;
// find the requested range
// this might be too simplistic, apparently the client can request
// multiple ranges, which can become pretty complex, so ignore it for now
preg_match('/bytes=(\d+)-(\d+)?/', $_SERVER['HTTP_RANGE'], $matches);
$offset = intval($matches[1]);
$length = intval($matches[2]) - $offset;
} else {
$partialContent = false;
}
$file = fopen($file, 'r');
// seek to the requested offset, this is 0 if it's not a partial content request
fseek($file, $offset);
$data = fread($file, $length);
fclose($file);
if ( $partialContent ) {
// output the right headers for partial content
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $offset . '-' . ($offset + $length) . '/' . $filesize);
}
// output the regular HTTP headers
header('Content-Type: ' . $ctype);
header('Content-Length: ' . $filesize);
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Accept-Ranges: bytes');
// don't forget to send the data too
print($data);
Mogłem przeoczyć coś oczywistego i zdecydowanie zignorowałem niektóre potencjalne źródła błędów, ale to powinien być początek.
Jest tutaj opis częściowej zawartości i znalazłem informacje na temat częściowej zawartości na stronie dokumentacji dla fread .
$length
będzie ujemny.$length = (($matches[2]) ? intval($matches[2]) : $filesize) - $offset;
naprawia to. 2.Content-Range
traktuje pierwszy bajt jako bajt0
, więc ostatni bajt jest$filesize - 1
. Dlatego tak musi być($offset + $length - 1)
.EDYCJA 2017/01 - Napisałem bibliotekę, aby to zrobić w PHP> = 7.0 https://github.com/DaveRandom/Resume
EDYCJA 2016/02 - Kod całkowicie przepisany na zestaw narzędzi modułowych jako przykład użycia zamiast funkcji monolitycznej. Wprowadzono poprawki wymienione w komentarzach poniżej.
Przetestowane, działające rozwiązanie (w dużej mierze oparte na powyższej odpowiedzi Theo), które zajmuje się wznowieniem pobierania, w zestawie kilku samodzielnych narzędzi. Ten kod wymaga PHP w wersji 5.4 lub nowszej.
To rozwiązanie nadal radzi sobie tylko z jednym zakresem na żądanie, ale w każdych okolicznościach ze standardową przeglądarką, o której mogę pomyśleć, nie powinno to powodować problemu.
Przykładowe użycie:
źródło
$start = $end - intval($range[0]);
powinna brzmiećrange[1]
To działa w 100% super sprawdź, używam i nie ma już problemów.
źródło
Tak. Wsparcie byteranges. Zobacz RFC 2616, sekcja 14.35 .
Zasadniczo oznacza to, że powinieneś przeczytać
Range
nagłówek i rozpocząć obsługę pliku od określonego przesunięcia.Oznacza to, że nie możesz użyć readfile (), ponieważ obsługuje on cały plik. Zamiast tego najpierw użyj fopen () , następnie fseek () do właściwej pozycji, a następnie użyj fpassthru () do wyświetlenia pliku.
źródło
echo file_get_contents(...)
nie działa (OOM). Więc nie sądzę, żeby to był problem. PHP 5.3.0fpassthru
nie powiedzie się nawet na plikach 50 MB. Zdecydowanie nie powinieneś go używać, jeśli obsługujesz duże pliki na słabej konfiguracji serwera. Jak słusznie zauważa @Wimmer,fread
+print
to wszystko, czego potrzebujesz w tym przypadku.Naprawdę fajnym sposobem rozwiązania tego problemu bez konieczności „rozwijania własnego” kodu PHP jest użycie modułu mod_xsendfile Apache. Następnie w PHP wystarczy ustawić odpowiednie nagłówki. Apache robi swoje.
źródło
XSendFilePath <absolute path> [AllowFileDelete]
( tn123.org/mod_xsendfile/beta ).Jeśli chcesz zainstalować nowy moduł PECL, najłatwiejszym sposobem obsługi pobierania wznawialnego za pomocą PHP jest wykonanie w
http_send_file()
ten sposóbźródło: http://www.php.net/manual/en/function.http-send-file.php
Używamy go do obsługi treści przechowywanych w bazie danych i działa jak marzenie!
źródło
Najlepsza odpowiedź zawiera różne błędy.
bytes a-b
powinno oznaczać[a, b]
zamiast[a, b)
ibytes a-
nie jest obsługiwane.Oto mój zmodyfikowany kod:
źródło
ini_set('memory_limit', '-1');
?if(!isset($matches[2])) { $end=$fs-1; } else { $end = intval($matches[2]); }
Tak, możesz do tego użyć nagłówka Range. Aby uzyskać pełne pobranie, musisz przekazać klientowi 3 dodatkowe nagłówki:
Niż w przypadku przerwanego pobierania należy sprawdzić nagłówek żądania zakresu przez:
I w tym przypadku nie zapomnij podać treści z kodem stanu 206:
Otrzymasz zmienne $ start i $ to z nagłówka żądania i użyjesz fseek (), aby znaleźć właściwą pozycję w pliku.
źródło
To działało bardzo dobrze dla mnie: https://github.com/pomle/php-serveFilePartial
źródło
Klasa z włączoną funkcją Small Composer, która działa tak samo, jak http_send_file pecl. Oznacza to obsługę wznawiania pobierania i ograniczania przepustowości. https://github.com/diversen/http-send-file
źródło
Wznowienie pobierania w HTTP odbywa się za pośrednictwem
Range
nagłówka. Jeżeli wniosek zawieraRange
nagłówek, a jeśli inne wskaźniki (npIf-Match
,If-Unmodified-Since
) wskazują, że zawartość nie uległa zmianie, ponieważ pobieranie rozpoczęto, podać kod 206 odpowiedzi (zamiast 200), wskazać zakres bajtów wracasz wContent-Range
nagłówku, a następnie podaj ten zakres w treści odpowiedzi.Nie wiem jednak, jak to zrobić w PHP.
źródło
Dzięki Theo! twoja metoda nie działała bezpośrednio w przypadku strumieniowego przesyłania danych divx, ponieważ odkryłem, że odtwarzacz divx wysyła zakresy takie jak bajty = 9932800-
ale pokazał mi, jak to zrobić, dzięki: D
źródło
Możesz użyć poniższego kodu do obsługi żądań zakresu bajtów w dowolnej przeglądarce
źródło