Remote-Dateigröße ohne Herunterladen der Datei


Antworten:


100

Hier etwas dazu gefunden :

Hier ist der beste Weg (den ich gefunden habe), um die Größe einer entfernten Datei zu erhalten. Beachten Sie, dass HEAD-Anforderungen nicht den tatsächlichen Hauptteil der Anforderung erhalten, sondern nur die Header abrufen. Das Erstellen einer HEAD-Anforderung an eine Ressource mit 100 MB dauert also genauso lange wie eine HEAD-Anforderung an eine Ressource mit 1 KB.

<?php
/**
 * Returns the size of a file without downloading it, or -1 if the file
 * size could not be determined.
 *
 * @param $url - The location of the remote file to download. Cannot
 * be null or empty.
 *
 * @return The size of the file referenced by $url, or -1 if the size
 * could not be determined.
 */
function curl_get_file_size( $url ) {
  // Assume failure.
  $result = -1;

  $curl = curl_init( $url );

  // Issue a HEAD request and follow any redirects.
  curl_setopt( $curl, CURLOPT_NOBODY, true );
  curl_setopt( $curl, CURLOPT_HEADER, true );
  curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
  curl_setopt( $curl, CURLOPT_FOLLOWLOCATION, true );
  curl_setopt( $curl, CURLOPT_USERAGENT, get_user_agent_string() );

  $data = curl_exec( $curl );
  curl_close( $curl );

  if( $data ) {
    $content_length = "unknown";
    $status = "unknown";

    if( preg_match( "/^HTTP\/1\.[01] (\d\d\d)/", $data, $matches ) ) {
      $status = (int)$matches[1];
    }

    if( preg_match( "/Content-Length: (\d+)/", $data, $matches ) ) {
      $content_length = (int)$matches[1];
    }

    // http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
    if( $status == 200 || ($status > 300 && $status <= 308) ) {
      $result = $content_length;
    }
  }

  return $result;
}
?>

Verwendung:

$file_size = curl_get_file_size( "http://stackoverflow.com/questions/2602612/php-remote-file-size-without-downloading-file" );

4
Beachten Sie jedoch, dass es Antworten ohne Inhaltslänge geben kann .
VolkerK

4
Wäre es nicht besser zu verwenden curl_getinfo, wie @macki vorschlägt?
Svish

1
@Svish, ja, weil dieser Ansatz tatsächlich funktioniert. Der hier vorgestellte Ansatz schlägt bei umgeleiteten URLs fehl, da die erste Inhaltslänge erfasst wird, die nicht (unbedingt?) Die endgültige Inhaltslänge ist. Durch meine Erfahrung.
Bobby Jack

11
Dies hat bei mir nicht funktioniert, wie get_user_agent_string()nicht definiert. Durch das Entfernen der gesamten Linie funktionierte das Ganze.
Rapti

1
Dies schlägt fehl, wenn getestet mit: http://www.dailymotion.com/rss/user/dialhainaut/siehe SO: stackoverflow.com/questions/36761377/…
ErickBest

63

Versuchen Sie diesen Code

function retrieve_remote_file_size($url){
     $ch = curl_init($url);

     curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
     curl_setopt($ch, CURLOPT_HEADER, TRUE);
     curl_setopt($ch, CURLOPT_NOBODY, TRUE);

     $data = curl_exec($ch);
     $size = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);

     curl_close($ch);
     return $size;
}

Wenn dies bei Ihnen nicht funktioniert, möchten Sie möglicherweise hinzufügen curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);.
Mermshaus

3
Funktioniert bei mir nicht für ein Bild. Ich habe CURLOPT_FOLLOWLOCATIONauf wahr gesetzt.
Nate

5
@Abenil diesen Parameter hinzufügen. curl_setopt ($ curl, CURLOPT_SSL_VERIFYPEER, false);
Davinder Kumar

1
@Davinder Kumar: Vielen Dank, wenn Sie Ihren Code hinzufügen, funktioniert der obige Code.
Trung Le Nguyen Nhat

1
Herzlich willkommen! @ TrungLeNguyenNhat
Davinder Kumar

31

Wie bereits einige Male erwähnt, müssen Sie die Informationen aus dem Content-LengthFeld des Antwortheaders abrufen .

Beachten Sie dies jedoch

  • Der Server, den Sie prüfen, implementiert nicht unbedingt die HEAD-Methode (!)
  • Es ist absolut nicht erforderlich, eine HEAD-Anfrage (die wiederum möglicherweise nicht einmal unterstützt wird) manuell fopenoder ähnlich zu erstellen oder die Curl-Bibliothek aufzurufen, wenn PHP dies getan hat get_headers()(denken Sie daran: KISS ).

Die Verwendung von get_headers()folgt dem KISS-Prinzip und funktioniert auch dann, wenn der Server, den Sie prüfen, die HEAD-Anforderung nicht unterstützt.

Also, hier ist meine Version (Gimmick: Gibt eine für Menschen lesbare formatierte Größe zurück ;-)):

Inhalt: https://gist.github.com/eyecatchup/f26300ffd7e50a92bc4d (Curl- und get_headers-Version)
get_headers () - Version:

<?php     
/**
 *  Get the file size of any remote resource (using get_headers()), 
 *  either in bytes or - default - as human-readable formatted string.
 *
 *  @author  Stephan Schmitz <eyecatchup@gmail.com>
 *  @license MIT <http://eyecatchup.mit-license.org/>
 *  @url     <https://gist.github.com/eyecatchup/f26300ffd7e50a92bc4d>
 *
 *  @param   string   $url          Takes the remote object's URL.
 *  @param   boolean  $formatSize   Whether to return size in bytes or formatted.
 *  @param   boolean  $useHead      Whether to use HEAD requests. If false, uses GET.
 *  @return  string                 Returns human-readable formatted size
 *                                  or size in bytes (default: formatted).
 */
function getRemoteFilesize($url, $formatSize = true, $useHead = true)
{
    if (false !== $useHead) {
        stream_context_set_default(array('http' => array('method' => 'HEAD')));
    }
    $head = array_change_key_case(get_headers($url, 1));
    // content-length of download (in bytes), read from Content-Length: field
    $clen = isset($head['content-length']) ? $head['content-length'] : 0;

    // cannot retrieve file size, return "-1"
    if (!$clen) {
        return -1;
    }

    if (!$formatSize) {
        return $clen; // return size in bytes
    }

    $size = $clen;
    switch ($clen) {
        case $clen < 1024:
            $size = $clen .' B'; break;
        case $clen < 1048576:
            $size = round($clen / 1024, 2) .' KiB'; break;
        case $clen < 1073741824:
            $size = round($clen / 1048576, 2) . ' MiB'; break;
        case $clen < 1099511627776:
            $size = round($clen / 1073741824, 2) . ' GiB'; break;
    }

    return $size; // return formatted size
}

Verwendung:

$url = 'http://download.tuxfamily.org/notepadplus/6.6.9/npp.6.6.9.Installer.exe';
echo getRemoteFilesize($url); // echoes "7.51 MiB"

Zusätzlicher Hinweis: Der Header "Content-Length" ist optional. Daher ist es als allgemeine Lösung nicht kugelsicher !



2
Dies sollte die akzeptierte Antwort sein. Richtig, Content-Lengthist optional, aber es ist der einzige Weg, die Dateigröße zu erhalten, ohne sie herunterzuladen - und get_headersder beste Weg, um sie zu erhalten content-length.
Quentin Skousen

2
Beachten Sie, dass dadurch die Präferenz für die Anforderungsmethode in allen nachfolgenden HTTP-Anforderungen für diesen PHP-Prozess in HEAD geändert wird. Verwenden Sie stream_context_creatediese Option , um einen separaten Kontext für den Aufruf von get_headers(7.1+) zu erstellen .
MatsLindh


15

Sicher. Stellen Sie eine reine Header-Anfrage und suchen Sie nach dem Content-LengthHeader.


14

PHP-Funktion get_headers()funktioniert für mich, um die Länge des Inhalts als zu überprüfen

$headers = get_headers('http://example.com/image.jpg', 1);
$filesize = $headers['Content-Length'];

Für mehr Details: PHP-Funktion get_headers ()


4
Für mich (mit Nginx) war der Header Content-Length
Pangamma

7

Ich bin mir nicht sicher, aber konnten Sie die Funktion get_headers dafür nicht verwenden?

$url     = 'http://example.com/dir/file.txt';
$headers = get_headers($url, true);

if ( isset($headers['Content-Length']) ) {
   $size = 'file size:' . $headers['Content-Length'];
}
else {
   $size = 'file size: unknown';
}

echo $size;

In diesem Beispiel kann der Zielserver unter $ url get_headers ausnutzen, um die Verbindung offen zu halten, bis der PHP-Prozess abläuft (indem die Header sehr langsam zurückgegeben werden, aber nicht langsam genug, um die Verbindung veralten zu lassen). Da die Gesamtzahl der PHP-Prozesse möglicherweise durch FPM begrenzt ist, kann dies eine Art langsamen Loris-Angriff ermöglichen, wenn mehrere "Benutzer" gleichzeitig auf Ihr get_headers-Skript zugreifen.
Ted Phillips

6

einzeilige beste Lösung:

echo array_change_key_case(get_headers("http://.../file.txt",1))['content-length'];

PHP ist zu delicius

function urlsize($url):int{
   return array_change_key_case(get_headers($url,1))['content-length'];
}

echo urlsize("http://.../file.txt");

3

Die einfachste und effizienteste Implementierung:

function remote_filesize($url, $fallback_to_download = false)
{
    static $regex = '/^Content-Length: *+\K\d++$/im';
    if (!$fp = @fopen($url, 'rb')) {
        return false;
    }
    if (isset($http_response_header) && preg_match($regex, implode("\n", $http_response_header), $matches)) {
        return (int)$matches[0];
    }
    if (!$fallback_to_download) {
        return false;
    }
    return strlen(stream_get_contents($fp));
}

OP zeigte "ohne die Datei herunterzuladen." Diese Methode lädt die Datei vom Remote-Server in den Speicher (z. B. Herunterladen). Selbst bei schnellen Verbindungen zwischen Servern kann dies bei großen Dateien leicht zu einer Zeitüberschreitung führen oder viel zu lange dauern. Hinweis: Sie haben $ fp nie geschlossen, was nicht im globalen Bereich liegt
Mavelo

1
Diese Funktion lädt den Körper NICHT so lange wie möglich herunter. wenn es Content-LengthHeader enthält . Ein explizites $fpSchließen ist NICHT NOTWENDIG. Es wird automatisch nach Ablauf freigegeben. php.net/manual/en/language.types.resource.php
mpyw

Sie können das oben genannte leicht mitnc -l localhost 8080
mpyw

Tatsächlich sind die meisten *closeFunktionen in modernem PHP nicht erforderlich. Sie haben zwei historische Gründe: Implementierungsbeschränkungen und Nachahmung der C-Sprache.
mpyw

Header sind unzuverlässig und der Fallback-Download widerspricht OP. Wenn Sie eine Datei öffnen, schließen Sie sie einfach. Garbage Collectors sind keine Entschuldigung für faule Entwickler, die eine einzige Codezeile speichern.
Mavelo

2

Da diese Frage bereits mit "php" und "curl" gekennzeichnet ist, gehe ich davon aus, dass Sie wissen, wie man Curl in PHP verwendet.

Wenn Sie festlegen, stellen curl_setopt(CURLOPT_NOBODY, TRUE)Sie eine HEAD-Anfrage und können wahrscheinlich den Header "Content-Length" der Antwort überprüfen, bei dem es sich nur um Header handelt.


2

Versuchen Sie die folgende Funktion, um die Größe der Remote-Datei zu ermitteln

function remote_file_size($url){
    $head = "";
    $url_p = parse_url($url);

    $host = $url_p["host"];
    if(!preg_match("/[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*/",$host)){

        $ip=gethostbyname($host);
        if(!preg_match("/[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*/",$ip)){

            return -1;
        }
    }
    if(isset($url_p["port"]))
    $port = intval($url_p["port"]);
    else
    $port    =    80;

    if(!$port) $port=80;
    $path = $url_p["path"];

    $fp = fsockopen($host, $port, $errno, $errstr, 20);
    if(!$fp) {
        return false;
        } else {
        fputs($fp, "HEAD "  . $url  . " HTTP/1.1\r\n");
        fputs($fp, "HOST: " . $host . "\r\n");
        fputs($fp, "User-Agent: http://www.example.com/my_application\r\n");
        fputs($fp, "Connection: close\r\n\r\n");
        $headers = "";
        while (!feof($fp)) {
            $headers .= fgets ($fp, 128);
            }
        }
    fclose ($fp);

    $return = -2;
    $arr_headers = explode("\n", $headers);
    foreach($arr_headers as $header) {

        $s1 = "HTTP/1.1";
        $s2 = "Content-Length: ";
        $s3 = "Location: ";

        if(substr(strtolower ($header), 0, strlen($s1)) == strtolower($s1)) $status = substr($header, strlen($s1));
        if(substr(strtolower ($header), 0, strlen($s2)) == strtolower($s2)) $size   = substr($header, strlen($s2));
        if(substr(strtolower ($header), 0, strlen($s3)) == strtolower($s3)) $newurl = substr($header, strlen($s3));  
    }

    if(intval($size) > 0) {
        $return=intval($size);
    } else {
        $return=$status;
    }

    if (intval($status)==302 && strlen($newurl) > 0) {

        $return = remote_file_size($newurl);
    }
    return $return;
}

Dies ist die einzige, die für mich auf Ubuntu Linux Apache Server funktioniert hat. Ich musste $ size und $ status zu Beginn der Funktion initialisieren, sonst funktionierte es so wie es ist.
Gavin Simpson

2

Hier ist ein weiterer Ansatz, der mit Servern funktioniert, die keine HEADAnforderungen unterstützen.

Es verwendet cURL, um eine Anforderung für den Inhalt mit einem HTTP-Bereichskopf zu stellen, der nach dem ersten Byte der Datei fragt.

Wenn der Server Bereichsanforderungen unterstützt (die meisten Medienserver werden dies tun), erhält er die Antwort mit der Größe der Ressource.

Wenn der Server nicht mit einem Bytebereich antwortet, sucht er nach einem Header mit Inhaltslänge, um die Länge zu bestimmen.

Wenn die Größe in einem Header für einen Bereich oder eine Inhaltslänge gefunden wird, wird die Übertragung abgebrochen. Wird die Größe nicht gefunden und die Funktion beginnt mit dem Lesen des Antwortkörpers, wird die Übertragung abgebrochen.

Dies kann ein zusätzlicher Ansatz sein, wenn eine HEADAnforderung zu einer 405nicht unterstützten Antwort führt.

/**
 * Try to determine the size of a remote file by making an HTTP request for
 * a byte range, or look for the content-length header in the response.
 * The function aborts the transfer as soon as the size is found, or if no
 * length headers are returned, it aborts the transfer.
 *
 * @return int|null null if size could not be determined, or length of content
 */
function getRemoteFileSize($url)
{
    $ch = curl_init($url);

    $headers = array(
        'Range: bytes=0-1',
        'Connection: close',
    );

    $in_headers = true;
    $size       = null;

    curl_setopt($ch, CURLOPT_HEADER, 1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2450.0 Iron/46.0.2450.0');
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_VERBOSE, 0); // set to 1 to debug
    curl_setopt($ch, CURLOPT_STDERR, fopen('php://output', 'r'));

    curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $line) use (&$in_headers, &$size) {
        $length = strlen($line);

        if (trim($line) == '') {
            $in_headers = false;
        }

        list($header, $content) = explode(':', $line, 2);
        $header = strtolower(trim($header));

        if ($header == 'content-range') {
            // found a content-range header
            list($rng, $s) = explode('/', $content, 2);
            $size = (int)$s;
            return 0; // aborts transfer
        } else if ($header == 'content-length' && 206 != curl_getinfo($curl, CURLINFO_HTTP_CODE)) {
            // found content-length header and this is not a 206 Partial Content response (range response)
            $size = (int)$content;
            return 0;
        } else {
            // continue
            return $length;
        }
    });

    curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($curl, $data) use ($in_headers) {
        if (!$in_headers) {
            // shouldn't be here unless we couldn't determine file size
            // abort transfer
            return 0;
        }

        // write function is also called when reading headers
        return strlen($data);
    });

    $result = curl_exec($ch);
    $info   = curl_getinfo($ch);

    return $size;
}

Verwendung:

$size = getRemoteFileSize('http://example.com/video.mp4');
if ($size === null) {
    echo "Could not determine file size from headers.";
} else {
    echo "File size is {$size} bytes.";
}

1
Ihre Antwort hat mir wirklich geholfen. Gibt immer die Antwort zurück. Auch wenn Content-Lengthnicht verfügbar ist.
Iman Hejazi

Hallo, danke fürs schauen und kommentieren. Ich bin wirklich froh, dass Sie es hilfreich fanden!
zog010

1

Die meisten Antworten hier verwenden entweder CURL oder basieren auf dem Lesen von Headern. In bestimmten Situationen können Sie jedoch eine viel einfachere Lösung verwenden. Beachten Sie den Hinweis zu filesize()den Dokumenten auf PHP.net . Dort finden Sie einen Tipp: " Ab PHP 5.0.0 kann diese Funktion auch mit einigen URL-Wrappern verwendet werden. Informationen zu den Wrappern, die die stat () -Familie von Funktionen unterstützen, finden Sie unter Unterstützte Protokolle und Wrapper ."

Wenn Ihr Server und Ihr PHP-Parser richtig konfiguriert sind, können Sie einfach die filesize()Funktion verwenden, sie mit der vollständigen URL versorgen, auf eine entfernte Datei zeigen, welche Größe Sie erhalten möchten, und PHP die ganze Magie machen lassen.


1

Versuchen Sie Folgendes: Ich benutze es und habe ein gutes Ergebnis erzielt.

    function getRemoteFilesize($url)
{
    $file_headers = @get_headers($url, 1);
    if($size =getSize($file_headers)){
return $size;
    } elseif($file_headers[0] == "HTTP/1.1 302 Found"){
        if (isset($file_headers["Location"])) {
            $url = $file_headers["Location"][0];
            if (strpos($url, "/_as/") !== false) {
                $url = substr($url, 0, strpos($url, "/_as/"));
            }
            $file_headers = @get_headers($url, 1);
            return getSize($file_headers);
        }
    }
    return false;
}

function getSize($file_headers){

    if (!$file_headers || $file_headers[0] == "HTTP/1.1 404 Not Found" || $file_headers[0] == "HTTP/1.0 404 Not Found") {
        return false;
    } elseif ($file_headers[0] == "HTTP/1.0 200 OK" || $file_headers[0] == "HTTP/1.1 200 OK") {

        $clen=(isset($file_headers['Content-Length']))?$file_headers['Content-Length']:false;
        $size = $clen;
        if($clen) {
            switch ($clen) {
                case $clen < 1024:
                    $size = $clen . ' B';
                    break;
                case $clen < 1048576:
                    $size = round($clen / 1024, 2) . ' KiB';
                    break;
                case $clen < 1073741824:
                    $size = round($clen / 1048576, 2) . ' MiB';
                    break;
                case $clen < 1099511627776:
                    $size = round($clen / 1073741824, 2) . ' GiB';
                    break;
            }
        }
        return $size;

    }
    return false;
}

Testen Sie nun wie folgt:

echo getRemoteFilesize('http://mandasoy.com/wp-content/themes/spacious/images/plain.png').PHP_EOL;
echo getRemoteFilesize('http://bookfi.net/dl/201893/e96818').PHP_EOL;
echo getRemoteFilesize('/programming/14679268/downloading-files-as-attachment-filesize-incorrect').PHP_EOL;

Ergebnisse:

24,82 KiB

912 KiB

101,85 KiB


1

Um die HTTP / 2-Anforderung abzudecken, muss die hier bereitgestellte Funktion https://stackoverflow.com/a/2602624/2380767 ein wenig geändert werden:

<?php
/**
 * Returns the size of a file without downloading it, or -1 if the file
 * size could not be determined.
 *
 * @param $url - The location of the remote file to download. Cannot
 * be null or empty.
 *
 * @return The size of the file referenced by $url, or -1 if the size
 * could not be determined.
 */
function curl_get_file_size( $url ) {
  // Assume failure.
  $result = -1;

  $curl = curl_init( $url );

  // Issue a HEAD request and follow any redirects.
  curl_setopt( $curl, CURLOPT_NOBODY, true );
  curl_setopt( $curl, CURLOPT_HEADER, true );
  curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
  curl_setopt( $curl, CURLOPT_FOLLOWLOCATION, true );
  curl_setopt( $curl, CURLOPT_USERAGENT, get_user_agent_string() );

  $data = curl_exec( $curl );
  curl_close( $curl );

  if( $data ) {
    $content_length = "unknown";
    $status = "unknown";

    if( preg_match( "/^HTTP\/1\.[01] (\d\d\d)/", $data, $matches ) ) {
      $status = (int)$matches[1];
    } elseif( preg_match( "/^HTTP\/2 (\d\d\d)/", $data, $matches ) ) {
      $status = (int)$matches[1];
    }

    if( preg_match( "/Content-Length: (\d+)/", $data, $matches ) ) {
      $content_length = (int)$matches[1];
    } elseif( preg_match( "/content-length: (\d+)/", $data, $matches ) ) {
        $content_length = (int)$matches[1];
    }

    // http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
    if( $status == 200 || ($status > 300 && $status <= 308) ) {
      $result = $content_length;
    }
  }

  return $result;
}
?>
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.