Fortsetzbare Downloads bei Verwendung von PHP zum Senden der Datei?

Lesezeit: 15 Minuten

Wir verwenden ein PHP-Skript zum Tunneln von Dateidownloads, da wir den absoluten Pfad der herunterladbaren Datei nicht offenlegen möchten:

header("Content-Type: $ctype");
header("Content-Length: " . filesize($file));
header("Content-Disposition: attachment; filename=\"$fileName\"");
readfile($file);

Leider haben wir festgestellt, dass Downloads, die durch dieses Skript geleitet werden, vom Endbenutzer nicht fortgesetzt werden können.

Gibt es eine Möglichkeit, fortsetzbare Downloads mit einer solchen PHP-basierten Lösung zu unterstützen?

Fortsetzbare Downloads bei Verwendung von PHP zum Senden der Datei
Das Ö

Das erste, was Sie tun müssen, ist, die zu senden Accept-Ranges: bytes Header in allen Antworten, um dem Client mitzuteilen, dass Sie partielle Inhalte unterstützen. Dann auf Wunsch mit a Range: bytes=x-y Header empfangen wird (mit x und y Zahlen sind) analysieren Sie den Bereich, den der Client anfordert, öffnen Sie die Datei wie gewohnt, suchen Sie x Bytes voraus und senden Sie die nächsten yx Byte. Stellen Sie auch die Antwort auf ein HTTP/1.0 206 Partial Content.

Ohne etwas getestet zu haben, könnte dies mehr oder weniger funktionieren:

$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) . "https://stackoverflow.com/" . $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);

Ich habe vielleicht etwas Offensichtliches übersehen, und ich habe definitiv einige potenzielle Fehlerquellen ignoriert, aber es sollte ein Anfang sein.

Da ist ein Beschreibung von Teilinhalten hier und ich habe einige Informationen zu teilweisen Inhalten auf der Dokumentationsseite für gefunden Angst.

  • Kleiner Fehler, Ihr regulärer Ausdruck sollte lauten: preg_match(‘/bytes=(\d+)-(\d+)?/’, $_SERVER[‘HTTP_RANGE’]$matches)

    – Tiefbrunnen

    13. Juli 2009 um 22:46 Uhr

  • Du hast Recht und ich habe es geändert. Allerdings ist es mir sowieso zu einfach, laut den Spezifikationen können Sie “bytes=xy”, “bytes=-x”, “bytes=x-“, “bytes=xy,ab” usw. tun, also den Fehler in der vorherigen Version war der fehlende Schrägstrich am Ende, nicht das Fehlen eines Fragezeichens.

    – Das Ö

    16. Juli 2009 um 8:09 Uhr

  • Sehr hilfreich, aber ich musste zwei kleine Änderungen vornehmen, damit es funktioniert: 1. Wenn der Client den Endpunkt nicht im Bereich sendet (da er implizit ist), $length wird negativ sein. $length = (($matches[2]) ? intval($matches[2]) : $filesize) - $offset; behebt das. 2. Content-Range behandelt das erste Byte als Byte 0also das letzte Byte ist $filesize - 1. Daher muss es sein ($offset + $length - 1).

    – Denis

    7. März 2012 um 13:48 Uhr

  • Oben funktioniert es bei großen Downloads nicht, Sie erhalten einen “Schwerwiegenden PHP-Fehler: Zulässige Speichergröße von XXXX Bytes erschöpft (versucht, XXX Bytes zuzuweisen) in “. In meinem Fall waren 100 MB zu groß. Sie speichern im Grunde alle Dateien in einer Variablen und spucken sie aus.

    – sarah.ferguson

    29. Januar 2016 um 16:56 Uhr

  • Sie können das Problem mit großen Dateien lösen, indem Sie sie in Stücken statt auf einmal auslesen.

    – dynamische Hael

    23. September 2018 um 6:36 Uhr

Fortsetzbare Downloads bei Verwendung von PHP zum Senden der Datei
DaveRandom

BEARBEITEN 01.2017 – Ich habe dazu eine Bibliothek in PHP >=7.0 geschrieben https://github.com/DaveRandom/Resume

BEARBEITEN 2016/02 – Code komplett umgeschrieben in eine Reihe von modularen Tools und eine beispielhafte Verwendung, anstatt eine monolithische Funktion. In den Kommentaren unten erwähnte Korrekturen wurden eingearbeitet.


Eine getestete, funktionierende Lösung (die stark auf Theos Antwort oben basiert), die sich mit fortsetzbaren Downloads befasst, in einer Reihe einiger eigenständiger Tools. Dieser Code erfordert PHP 5.4 oder höher.

Diese Lösung kann immer noch nur einen Bereich pro Anfrage bewältigen, aber unter allen Umständen mit einem Standardbrowser, den ich mir vorstellen kann, sollte dies kein Problem verursachen.

<?php

/**
 * Get the value of a header in the current request context
 *
 * @param string $name Name of the header
 * @return string|null Returns null when the header was not sent or cannot be retrieved
 */
function get_request_header($name)
{
    $name = strtoupper($name);

    // IIS/Some Apache versions and configurations
    if (isset($_SERVER['HTTP_' . $name])) {
        return trim($_SERVER['HTTP_' . $name]);
    }

    // Various other SAPIs
    foreach (apache_request_headers() as $header_name => $value) {
        if (strtoupper($header_name) === $name) {
            return trim($value);
        }
    }

    return null;
}

class NonExistentFileException extends \RuntimeException {}
class UnreadableFileException extends \RuntimeException {}
class UnsatisfiableRangeException extends \RuntimeException {}
class InvalidRangeHeaderException extends \RuntimeException {}

class RangeHeader
{
    /**
     * The first byte in the file to send (0-indexed), a null value indicates the last
     * $end bytes
     *
     * @var int|null
     */
    private $firstByte;

    /**
     * The last byte in the file to send (0-indexed), a null value indicates $start to
     * EOF
     *
     * @var int|null
     */
    private $lastByte;

    /**
     * Create a new instance from a Range header string
     *
     * @param string $header
     * @return RangeHeader
     */
    public static function createFromHeaderString($header)
    {
        if ($header === null) {
            return null;
        }

        if (!preg_match('/^\s*(\S+)\s*(\d*)\s*-\s*(\d*)\s*(?:,|$)/', $header, $info)) {
            throw new InvalidRangeHeaderException('Invalid header format');
        } else if (strtolower($info[1]) !== 'bytes') {
            throw new InvalidRangeHeaderException('Unknown range unit: ' . $info[1]);
        }

        return new self(
            $info[2] === '' ? null : $info[2],
            $info[3] === '' ? null : $info[3]
        );
    }

    /**
     * @param int|null $firstByte
     * @param int|null $lastByte
     * @throws InvalidRangeHeaderException
     */
    public function __construct($firstByte, $lastByte)
    {
        $this->firstByte = $firstByte === null ? $firstByte : (int)$firstByte;
        $this->lastByte = $lastByte === null ? $lastByte : (int)$lastByte;

        if ($this->firstByte === null && $this->lastByte === null) {
            throw new InvalidRangeHeaderException(
                'Both start and end position specifiers empty'
            );
        } else if ($this->firstByte < 0 || $this->lastByte < 0) {
            throw new InvalidRangeHeaderException(
                'Position specifiers cannot be negative'
            );
        } else if ($this->lastByte !== null && $this->lastByte < $this->firstByte) {
            throw new InvalidRangeHeaderException(
                'Last byte cannot be less than first byte'
            );
        }
    }

    /**
     * Get the start position when this range is applied to a file of the specified size
     *
     * @param int $fileSize
     * @return int
     * @throws UnsatisfiableRangeException
     */
    public function getStartPosition($fileSize)
    {
        $size = (int)$fileSize;

        if ($this->firstByte === null) {
            return ($size - 1) - $this->lastByte;
        }

        if ($size <= $this->firstByte) {
            throw new UnsatisfiableRangeException(
                'Start position is after the end of the file'
            );
        }

        return $this->firstByte;
    }

    /**
     * Get the end position when this range is applied to a file of the specified size
     *
     * @param int $fileSize
     * @return int
     * @throws UnsatisfiableRangeException
     */
    public function getEndPosition($fileSize)
    {
        $size = (int)$fileSize;

        if ($this->lastByte === null) {
            return $size - 1;
        }

        if ($size <= $this->lastByte) {
            throw new UnsatisfiableRangeException(
                'End position is after the end of the file'
            );
        }

        return $this->lastByte;
    }

    /**
     * Get the length when this range is applied to a file of the specified size
     *
     * @param int $fileSize
     * @return int
     * @throws UnsatisfiableRangeException
     */
    public function getLength($fileSize)
    {
        $size = (int)$fileSize;

        return $this->getEndPosition($size) - $this->getStartPosition($size) + 1;
    }

    /**
     * Get a Content-Range header corresponding to this Range and the specified file
     * size
     *
     * @param int $fileSize
     * @return string
     */
    public function getContentRangeHeader($fileSize)
    {
        return 'bytes ' . $this->getStartPosition($fileSize) . '-'
             . $this->getEndPosition($fileSize) . "https://stackoverflow.com/" . $fileSize;
    }
}

class PartialFileServlet
{
    /**
     * The range header on which the data transmission will be based
     *
     * @var RangeHeader|null
     */
    private $range;

    /**
     * @param RangeHeader $range Range header on which the transmission will be based
     */
    public function __construct(RangeHeader $range = null)
    {
        $this->range = $range;
    }

    /**
     * Send part of the data in a seekable stream resource to the output buffer
     *
     * @param resource $fp Stream resource to read data from
     * @param int $start Position in the stream to start reading
     * @param int $length Number of bytes to read
     * @param int $chunkSize Maximum bytes to read from the file in a single operation
     */
    private function sendDataRange($fp, $start, $length, $chunkSize = 8192)
    {
        if ($start > 0) {
            fseek($fp, $start, SEEK_SET);
        }

        while ($length) {
            $read = ($length > $chunkSize) ? $chunkSize : $length;
            $length -= $read;
            echo fread($fp, $read);
        }
    }

    /**
     * Send the headers that are included regardless of whether a range was requested
     *
     * @param string $fileName
     * @param int $contentLength
     * @param string $contentType
     */
    private function sendDownloadHeaders($fileName, $contentLength, $contentType)
    {
        header('Content-Type: ' . $contentType);
        header('Content-Length: ' . $contentLength);
        header('Content-Disposition: attachment; filename="' . $fileName . '"');
        header('Accept-Ranges: bytes');
    }

    /**
     * Send data from a file based on the current Range header
     *
     * @param string $path Local file system path to serve
     * @param string $contentType MIME type of the data stream
     */
    public function sendFile($path, $contentType="application/octet-stream")
    {
        // Make sure the file exists and is a file, otherwise we are wasting our time
        $localPath = realpath($path);
        if ($localPath === false || !is_file($localPath)) {
            throw new NonExistentFileException(
                $path . ' does not exist or is not a file'
            );
        }

        // Make sure we can open the file for reading
        if (!$fp = fopen($localPath, 'r')) {
            throw new UnreadableFileException(
                'Failed to open ' . $localPath . ' for reading'
            );
        }

        $fileSize = filesize($localPath);

        if ($this->range == null) {
            // No range requested, just send the whole file
            header('HTTP/1.1 200 OK');
            $this->sendDownloadHeaders(basename($localPath), $fileSize, $contentType);

            fpassthru($fp);
        } else {
            // Send the request range
            header('HTTP/1.1 206 Partial Content');
            header('Content-Range: ' . $this->range->getContentRangeHeader($fileSize));
            $this->sendDownloadHeaders(
                basename($localPath),
                $this->range->getLength($fileSize),
                $contentType
            );

            $this->sendDataRange(
                $fp,
                $this->range->getStartPosition($fileSize),
                $this->range->getLength($fileSize)
            );
        }

        fclose($fp);
    }
}

Beispielnutzung:

<?php

$path="/local/path/to/file.ext";
$contentType="application/octet-stream";

// Avoid sending unexpected errors to the client - we should be serving a file,
// we don't want to corrupt the data we send
ini_set('display_errors', '0');

try {
    $rangeHeader = RangeHeader::createFromHeaderString(get_request_header('Range'));
    (new PartialFileServlet($rangeHeader))->sendFile($path, $contentType);
} catch (InvalidRangeHeaderException $e) {
    header("HTTP/1.1 400 Bad Request");
} catch (UnsatisfiableRangeException $e) {
    header("HTTP/1.1 416 Range Not Satisfiable");
} catch (NonExistentFileException $e) {
    header("HTTP/1.1 404 Not Found");
} catch (UnreadableFileException $e) {
    header("HTTP/1.1 500 Internal Server Error");
}

// It's usually a good idea to explicitly exit after sending a file to avoid sending any
// extra data on the end that might corrupt the file
exit;

  • Ziemlich netter Code hier. Ich habe einen Fehler in der Zeile gefunden, in der $length gesetzt ist. Sollte lauten: $länge = $ende – $start + 1;

    – Bobwienholt

    11. Mai 2012 um 1:59 Uhr


  • Wie kann ich den Download pausieren?

    – Prasanth Bendra

    18. Februar 2013 um 11:32 Uhr

  • Sollte Content-Length auf die tatsächliche Dateigröße eingestellt werden oder nur auf die Anzahl der gesendeten Teilbytes? Diese Seite lässt es so aussehen, als ob es sich um die partiellen Bytes handeln sollte, aber das ist im obigen Beispielcode nicht der Fall. w3.org/Protocols/rfc2616/rfc2616-sec14.html

    – Willen

    21. Dezember 2013 um 15:18 Uhr


  • Noch ein kleiner Tippfehler: $start = $end - intval($range[0]); sollte sein range[1]

    – BurninLeo

    3. März 2014 um 14:29 Uhr


  • @sarah.ferguson Code komplett neu geschrieben und aktualisiert, siehe oben.

    – DaveRandom

    1. Februar 2016 um 15:15 Uhr

1646811249 280 Fortsetzbare Downloads bei Verwendung von PHP zum Senden der Datei
Sietse

Jawohl. Byteranges unterstützen. Sehen RFC 2616 Abschnitt 14.35 .

Es bedeutet im Grunde, dass Sie die lesen sollten Range -Header und starten Sie die Bereitstellung der Datei ab dem angegebenen Offset.

Das bedeutet, dass Sie readfile() nicht verwenden können, da dies die gesamte Datei bedient. Verwenden Sie stattdessen fopen() Zuerst, dann fseek() in die richtige Position bringen und dann verwenden fpassthru() um die Akte zu bedienen.

  • fpassthru ist keine gute Idee, wenn die Datei mehrere Megabyte groß ist, da Ihnen möglicherweise der Arbeitsspeicher ausgeht. Einfach fread() und print() in Stücken.

    – Willem

    12. Oktober 2008 um 22:58 Uhr

  • fpassthru funktioniert hier hervorragend mit Hunderten von Megabytes. echo file_get_contents(...) hat nicht funktioniert (OOM). Also ich denke nicht, dass das ein Problem ist. PHP5.3.

    – Janus Troelsen

    16. September 2011 um 12:52 Uhr

  • @JanusTroelsen Nein, ist es nicht. Es hängt alles von der Konfiguration Ihres Servers ab. Wenn Sie einen starken Server mit viel Speicher für PHP haben, funktioniert es vielleicht gut für Sie. Auf “schwache” Konfigurationen (wörtlich: Shared Hostings) verwenden fpassthru schlägt sogar bei Dateien mit 50 MB fehl. Sie sollten es definitiv nicht verwenden, wenn Sie große Dateien auf einer schwachen Serverkonfiguration bereitstellen. Wie @Wimmer richtig betont, fread + print ist alles, was Sie in diesem Fall brauchen.

    – trejder

    25. Februar 2014 um 8:04 Uhr

  • @trejder: Siehe die Notiz auf readfile(): readfile() wird selbst beim Senden großer Dateien keine Speicherprobleme verursachen. Wenn Sie auf einen Speichermangelfehler stoßen, stellen Sie sicher, dass die Ausgabepufferung mit ob_get_level() ausgeschaltet ist.

    – Janus Troelsen

    25. Februar 2014 um 9:05 Uhr

  • @trejder das Problem ist, dass Sie Ihre Ausgabepufferung nicht richtig konfiguriert haben. Es führt das Chunking automatisch durch, wenn Sie es anweisen: php.net/manual/de/… zB output_buffering=4096 (und wenn Ihr Framework dies nicht zulässt, ist Ihr Framework scheiße)

    – ZJR

    14. Oktober 2015 um 17:00 Uhr

1646811250 164 Fortsetzbare Downloads bei Verwendung von PHP zum Senden der Datei
Lebenslehrer

Das funktioniert zu 100% super, überprüfen Sie es, ich benutze es und keine Probleme mehr.

        /* Function: download with resume/speed/stream options */


         /* List of File Types */
        function fileTypes($extension){
            $fileTypes['swf'] = 'application/x-shockwave-flash';
            $fileTypes['pdf'] = 'application/pdf';
            $fileTypes['exe'] = 'application/octet-stream';
            $fileTypes['zip'] = 'application/zip';
            $fileTypes['doc'] = 'application/msword';
            $fileTypes['xls'] = 'application/vnd.ms-excel';
            $fileTypes['ppt'] = 'application/vnd.ms-powerpoint';
            $fileTypes['gif'] = 'image/gif';
            $fileTypes['png'] = 'image/png';
            $fileTypes['jpeg'] = 'image/jpg';
            $fileTypes['jpg'] = 'image/jpg';
            $fileTypes['rar'] = 'application/rar';

            $fileTypes['ra'] = 'audio/x-pn-realaudio';
            $fileTypes['ram'] = 'audio/x-pn-realaudio';
            $fileTypes['ogg'] = 'audio/x-pn-realaudio';

            $fileTypes['wav'] = 'video/x-msvideo';
            $fileTypes['wmv'] = 'video/x-msvideo';
            $fileTypes['avi'] = 'video/x-msvideo';
            $fileTypes['asf'] = 'video/x-msvideo';
            $fileTypes['divx'] = 'video/x-msvideo';

            $fileTypes['mp3'] = 'audio/mpeg';
            $fileTypes['mp4'] = 'audio/mpeg';
            $fileTypes['mpeg'] = 'video/mpeg';
            $fileTypes['mpg'] = 'video/mpeg';
            $fileTypes['mpe'] = 'video/mpeg';
            $fileTypes['mov'] = 'video/quicktime';
            $fileTypes['swf'] = 'video/quicktime';
            $fileTypes['3gp'] = 'video/quicktime';
            $fileTypes['m4a'] = 'video/quicktime';
            $fileTypes['aac'] = 'video/quicktime';
            $fileTypes['m3u'] = 'video/quicktime';
            return $fileTypes[$extention];
        };

        /*
          Parameters: downloadFile(File Location, File Name,
          max speed, is streaming
          If streaming - videos will show as videos, images as images
          instead of download prompt
         */

        function downloadFile($fileLocation, $fileName, $maxSpeed = 100, $doStream = false) {
            if (connection_status() != 0)
                return(false);
        //    in some old versions this can be pereferable to get extention
        //    $extension = strtolower(end(explode('.', $fileName)));
            $extension = pathinfo($fileName, PATHINFO_EXTENSION);

            $contentType = fileTypes($extension);
            header("Cache-Control: public");
            header("Content-Transfer-Encoding: binary\n");
            header('Content-Type: $contentType');

            $contentDisposition = 'attachment';

            if ($doStream == true) {
                /* extensions to stream */
                $array_listen = array('mp3', 'm3u', 'm4a', 'mid', 'ogg', 'ra', 'ram', 'wm',
                    'wav', 'wma', 'aac', '3gp', 'avi', 'mov', 'mp4', 'mpeg', 'mpg', 'swf', 'wmv', 'divx', 'asf');
                if (in_array($extension, $array_listen)) {
                    $contentDisposition = 'inline';
                }
            }

            if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE")) {
                $fileName = preg_replace('/\./', '%2e', $fileName, substr_count($fileName, '.') - 1);
                header("Content-Disposition: $contentDisposition;
                    filename=\"$fileName\"");
            } else {
                header("Content-Disposition: $contentDisposition;
                    filename=\"$fileName\"");
            }

            header("Accept-Ranges: bytes");
            $range = 0;
            $size = filesize($fileLocation);

            if (isset($_SERVER['HTTP_RANGE'])) {
                list($a, $range) = explode("=", $_SERVER['HTTP_RANGE']);
                str_replace($range, "-", $range);
                $size2 = $size - 1;
                $new_length = $size - $range;
                header("HTTP/1.1 206 Partial Content");
                header("Content-Length: $new_length");
                header("Content-Range: bytes $range$size2/$size");
            } else {
                $size2 = $size - 1;
                header("Content-Range: bytes 0-$size2/$size");
                header("Content-Length: " . $size);
            }

            if ($size == 0) {
                die('Zero byte file! Aborting download');
            }
            set_magic_quotes_runtime(0);
            $fp = fopen("$fileLocation", "rb");

            fseek($fp, $range);

            while (!feof($fp) and ( connection_status() == 0)) {
                set_time_limit(0);
                print(fread($fp, 1024 * $maxSpeed));
                flush();
                ob_flush();
                sleep(1);
            }
            fclose($fp);

            return((connection_status() == 0) and ! connection_aborted());
        }

        /* Implementation */
        // downloadFile('path_to_file/1.mp3', '1.mp3', 1024, false);

Eine wirklich gute Möglichkeit, dies zu lösen, ohne Ihren eigenen PHP-Code „rollen“ zu müssen, ist die Verwendung des Apache-Moduls mod_xsendfile. Dann setzen Sie in PHP einfach die entsprechenden Header. Apache darf sein Ding machen.

header("X-Sendfile: /path/to/file");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; file=\"filename\"");

  • Was ist, wenn Sie die Verknüpfung der Datei nach dem Senden aufheben möchten?

    – Janus Troelsen

    16. September 2011 um 12:54 Uhr

  • Wenn Sie die Verknüpfung der Datei nach dem Senden aufheben möchten, benötigen Sie ein spezielles Flag, um dies anzuzeigen, siehe XSendFilePath <absolute path> [AllowFileDelete] (tn123.org/mod_xsendfile/beta).

    – Jens A. Koch

    26. Juli 2016 um 11:38 Uhr

Fortsetzbare Downloads bei Verwendung von PHP zum Senden der Datei
Mikko Rantalainen

Wenn Sie bereit sind, ein neues PECL-Modul zu installieren, wird die einfachste Weg, um fortsetzbare Downloads mit PHP zu unterstützen ist durch http_send_file()so was

<?php
http_send_content_disposition("document.pdf", true);
http_send_content_type("application/pdf");
http_throttle(0.1, 2048);
http_send_file("../report.pdf");
?>

Quelle : http://www.php.net/manual/en/function.http-send-file.php

Wir verwenden es, um in der Datenbank gespeicherte Inhalte bereitzustellen, und es funktioniert wie ein Zauber!

  • Was ist, wenn Sie die Verknüpfung der Datei nach dem Senden aufheben möchten?

    – Janus Troelsen

    16. September 2011 um 12:54 Uhr

  • Wenn Sie die Verknüpfung der Datei nach dem Senden aufheben möchten, benötigen Sie ein spezielles Flag, um dies anzuzeigen, siehe XSendFilePath <absolute path> [AllowFileDelete] (tn123.org/mod_xsendfile/beta).

    – Jens A. Koch

    26. Juli 2016 um 11:38 Uhr

1646811250 737 Fortsetzbare Downloads bei Verwendung von PHP zum Senden der Datei
Zsolt Szeberenyi

Ja, Sie können dafür den Range-Header verwenden. Für einen vollständigen Download müssen Sie dem Client 3 weitere Header geben:

header ("Accept-Ranges: bytes");
header ("Content-Length: " . $fileSize);
header ("Content-Range: bytes 0-" . $fileSize - 1 . "https://stackoverflow.com/" . $fileSize . ";");

Dann müssen Sie für einen unterbrochenen Download den Range-Request-Header überprüfen, indem Sie:

$headers = getAllHeaders ();
$range = substr ($headers['Range'], '6');

Und vergessen Sie in diesem Fall nicht, den Inhalt mit dem Statuscode 206 bereitzustellen:

header ("HTTP/1.1 206 Partial content");
header ("Accept-Ranges: bytes");
header ("Content-Length: " . $remaining_length);
header ("Content-Range: bytes " . $start . "-" . $to . "https://stackoverflow.com/" . $fileSize . ";");

Sie erhalten die Variablen $start und $to aus dem Anforderungsheader und verwenden fseek(), um nach der richtigen Position in der Datei zu suchen.

  • @ceejayoz: getallheaders() ist eine PHP-Funktion, die Sie erhalten, wenn Sie Apache verwenden uk2.php.net/getallheaders

    – Tom Haigh

    25. Oktober 2008 um 10:25 Uhr

981250cookie-checkFortsetzbare Downloads bei Verwendung von PHP zum Senden der Datei?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy