Dosya indirmeden uzak dosya boyutu


Yanıtlar:


100

Burada bununla ilgili bir şey buldum :

Uzak bir dosyanın boyutunu almanın en iyi yolu (bulduğum). HEAD isteklerinin, isteğin gerçek gövdesini almadığını, yalnızca başlıkları aldıklarını unutmayın. Bu nedenle, 100MB olan bir kaynağa HEAD isteğinde bulunmak, 1KB olan bir kaynağa yapılan HEAD isteğiyle aynı süreyi alacaktır.

<?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;
}
?>

Kullanım:

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

5
Ancak , İçerik uzunluğu olmadan yanıtlar olabileceğini unutmayın .
VolkerK

4
curl_getinfo@ Macki'nin önerdiği gibi kullanmak daha iyi olmaz mıydı ?
Svish

1
@Svish, evet, çünkü bu yaklaşım gerçekten işe yarıyor. Burada sunulan yaklaşım, yeniden yönlendirilen URL'lerde başarısız olur, çünkü (zorunlu olarak?) Nihai İçerik Uzunluğu olmayan ilk İçerik Uzunluğunu alır. Tecrübelerime göre.
Bobby Jack

12
Bu benim için get_user_agent_string()tanımlanmadığı gibi işe yaramadı . Tüm çizgiyi kaldırmak her şeyin yürümesini sağladı.
Rapti

2
sunucu
HEAD'i

63

Bu kodu dene

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;
}

Bu sizin için işe yaramazsa, eklemek isteyebilirsiniz curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);.
mermshaus

3
Benim için bir görüntü için çalışmıyor. Ben CURLOPT_FOLLOWLOCATIONdoğruyu ayarladım.
Nate

5
@Abenil bu parametreyi ekleyin. curl_setopt ($ curl, CURLOPT_SSL_VERIFYPEER, false);
Davinder Kumar

1
@Davinder Kumar: Çok teşekkürler, kodunuzu eklemek yukarıdaki kodun çalışmasını sağlar.
Trung Le Nguyen Nhat

1
Rica ederim! @TrungLeNguyenNhat
Davinder Kumar

31

Birkaç kez bahsedildiği gibi, gitmenin yolu, bilgiyi yanıt başlıklarının Content-Lengthalanından almaktır .

Ancak şunu not etmelisiniz

  • araştırdığınız sunucu HEAD yöntemini (!)
  • fopenPHP'nin sahip olduğu durumlarda curl kitaplığını kullanarak veya benzer şekilde bir HEAD isteğini (yine desteklenmeyebilir) manuel olarak oluşturmaya veya hatta çağırmaya kesinlikle gerek yoktur get_headers()(unutmayın: KISS )

Kullanımı get_headers(), KISS ilkesine göre yapılır ve araştırdığınız sunucu HEAD isteğini desteklemese bile çalışır.

İşte benim sürümüm (hile: insan tarafından okunabilir biçimlendirilmiş boyut döndürür ;-)):

Özet: https://gist.github.com/eyecatchup/f26300ffd7e50a92bc4d (curl ve get_headers sürümü)
get_headers () - Sürüm:

<?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
}

Kullanım:

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

Ek not: Content-Length üstbilgisi isteğe bağlıdır. Bu nedenle, genel bir çözüm olarak kurşun geçirmez değildir !



2
Kabul edilen cevap bu olmalıdır. Doğru, Content-Lengthisteğe bağlıdır, ancak dosya boyutunu indirmeden elde etmenin tek yolu ve almanın get_headersen iyi yolu budur content-length.
Quentin Skousen

2
Bunun, bu PHP işlemi için sonraki tüm HTTP isteklerinde istek yöntemi tercihini HEAD olarak değiştireceğini unutmayın. Kullanım stream_context_createiçin çağrı için kullanımına ayrı bir bağlam oluşturmak için get_headers(7.1+).
MatsLindh

URL'nizin veya BELGE dosya adınızın içinde boşluklar varsa, bunun bir -1 döndüreceğini
ekliyoruz

15

Elbette. Yalnızca başlıklar içeren bir istekte bulunun ve Content-Lengthbaşlığı arayın .


14

Php işlevi get_headers(), içerik uzunluğunu şu şekilde kontrol etmem için çalışıyor:

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

Daha Fazla Ayrıntı İçin: PHP İşlevi get_headers ()


4
Benim için (nginx ile) başlık Content-Length
Pangamma

7

Emin değilim, ama bunun için get_headers işlevini kullanamaz mısın?

$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;

Bu örnekle, $ url adresindeki hedef sunucunun get_headers'ı PHP işlemi zaman aşımına uğrayana kadar bağlantıyı açık tutmak için kullanması mümkündür (bağlantının bayatlamasına izin verecek kadar yavaş değilken başlıkları çok yavaş bir şekilde döndürerek). Toplam PHP işlemleri FPM tarafından sınırlandırılabileceğinden, bu, birden fazla "kullanıcı" aynı anda get_headers betiğinize eriştiğinde bir tür yavaş loris saldırısına izin verebilir.
Ted Phillips

6

tek satırlık en iyi çözüm:

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

php çok delicius

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

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

3

En basit ve en verimli uygulama:

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 "dosyayı indirmeden" belirtildi. Bu yöntem, dosyayı uzak sunucudan belleğe yükler (örneğin: indirme). Sunucular arasındaki hızlı bağlantılarda bile, bu kolayca zaman aşımına uğrayabilir veya büyük dosyalarda çok uzun sürebilir. Not: Küresel kapsamda olmayan $
fp'yi

1
Bu işlev gövdeyi mümkün olduğunca uzun süre YÜKLEMEZ; Content-Lengthbaşlık içeriyorsa . Ve kesin $fpkapatma GEREKLİ DEĞİLDİR; süresi dolduğunda otomatik olarak serbest bırakılır. php.net/manual/en/language.types.resource.php
mpyw

Yukarıdakileri kullanarak kolayca onaylayabilirsiniznc -l localhost 8080
mpyw

Aslında *closemodern PHP'de çoğu işlev gerekli değildir. Bunlar iki tarihsel nedenden kaynaklanmaktadır: uygulama kısıtlaması ve C dilini taklit etme.
mpyw

Başlıklar güvenilir değildir ve yedek indirme işlemi OP'ye aykırıdır. Son olarak, bir dosyayı açarsanız kapatın. Çöp toplayıcılar, tembel geliştiricilerin tek bir kod satırı tasarruf etmeleri için bir bahane değildir.
Mavelo

2

Bu soru zaten "php" ve "curl" olarak etiketlendiğinden, Curl'i PHP'de nasıl kullanacağınızı bildiğinizi varsayıyorum.

Ayarladıysanız curl_setopt(CURLOPT_NOBODY, TRUE), bir HEAD isteğinde bulunacaksınız ve muhtemelen yanıtın "Content-Length" başlığını kontrol edebilirsiniz, bu yalnızca başlıklar olacaktır.


2

Uzak dosya boyutunu elde etmek için aşağıdaki işlevi deneyin

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;
}

Ubuntu Linux apache sunucusunda benim için çalışan tek kişi budur. Fonksiyonun başında $ size ve $ statüsünü başlatmak zorunda kaldım, aksi halde olduğu gibi çalıştım.
Gavin Simpson

2

İşte HEADistekleri desteklemeyen sunucularla çalışacak başka bir yaklaşım .

Dosyanın ilk baytını soran bir HTTP aralığı başlığına sahip içerik için bir istek yapmak için cURL kullanır.

Sunucu, aralık isteklerini destekliyorsa (çoğu medya sunucusu destekleyecektir), o zaman kaynağın boyutuyla birlikte yanıtı alır.

Sunucu bir bayt aralığı ile yanıt vermezse, uzunluğu belirlemek için bir içerik uzunluğu başlığı arayacaktır.

Boyut bir aralıkta veya içerik uzunluğu üstbilgisinde bulunursa, aktarım durdurulur. Boyut bulunmazsa ve işlev yanıt gövdesini okumaya başlarsa, aktarım iptal edilir.

Bir HEADistek, 405desteklenmeyen bir yanıtla sonuçlanırsa, bu ek bir yaklaşım olabilir .

/**
 * 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;
}

Kullanım:

$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
Cevabınız bana gerçekten yardımcı oldu. Her zaman cevabı verir. Content-LengthMevcut olmasa bile .
Iman Hejazi

Merhaba, baktığınız ve yorum yaptığınız için teşekkürler. Bunu yararlı bulduğunuza gerçekten sevindim!
10

1

Buradaki yanıtların çoğu ya CURL kullanır ya da başlıkları okumaya dayanır. Ancak bazı durumlarda daha kolay bir çözüm kullanabilirsiniz. filesize()PHP.net'teki dokümanları üzerindeki notu düşünün . Orada şöyle bir ipucu bulacaksınız: " PHP 5.0.0'dan itibaren, bu işlev bazı URL sarmalayıcılarla da kullanılabilir. Hangi sarmalayıcıların stat () işlev ailesini desteklediğini belirlemek için Desteklenen Protokoller ve Sarmalayıcılar'a bakın ".

Dolayısıyla, sunucunuz ve PHP ayrıştırıcınız düzgün bir şekilde yapılandırılmışsa, filesize()işlevi kullanabilir , tam URL ile besleyebilir, hangi boyutta almak istediğinizi uzak bir dosyaya işaret edebilir ve PHP'nin tüm sihri yapmasına izin verebilirsiniz.


1

Deneyin: Kullanıyorum ve iyi sonuç aldım.

    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;
}

Şimdi, aşağıdaki gibi test edin:

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;

Sonuçlar:

24.82 KiB

912 KiB

101.85 KiB


1

HTTP / 2 isteğini karşılamak için https://stackoverflow.com/a/2602624/2380767 burada sağlanan işlevin biraz değiştirilmesi gerekiyor:

<?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;
}
?>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.