Kích thước tệp từ xa mà không cần tải tệp xuống


Câu trả lời:


100

Tìm thấy điều gì đó về điều này ở đây :

Đây là cách tốt nhất (mà tôi đã tìm thấy) để lấy kích thước của một tệp từ xa. Lưu ý rằng các yêu cầu HEAD không nhận được nội dung thực sự của yêu cầu, chúng chỉ truy xuất các tiêu đề. Vì vậy, việc thực hiện yêu cầu HEAD đối với tài nguyên có dung lượng 100MB sẽ mất cùng một khoảng thời gian như yêu cầu HEAD đối với tài nguyên là 1KB.

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

Sử dụng:

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

4
Nhưng hãy nhớ rằng có thể có các phản hồi mà không có thời lượng Nội dung.
VolkerK

4
Sẽ tốt hơn nếu sử dụng curl_getinfo, như @macki gợi ý?
Svish

1
@Svish, vâng, vì cách tiếp cận đó thực sự hiệu quả. Cách tiếp cận được trình bày ở đây không thành công trên các URL được chuyển hướng, vì nó lấy Độ dài nội dung đầu tiên mà không phải (nhất thiết?) Là Độ dài nội dung cuối cùng . Theo kinh nghiệm của tôi.
Bobby Jack

12
Điều này không làm việc cho tôi vì get_user_agent_string()không được xác định. Xóa toàn bộ dòng đã làm cho toàn bộ hoạt động.
Rapti

1
này không thành công khi thử nghiệm với: http://www.dailymotion.com/rss/user/dialhainaut/thấy SO: stackoverflow.com/questions/36761377/...
ErickBest

63

Hãy thử mã này

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

Nếu điều này không hiệu quả với bạn, bạn có thể muốn thêm curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);.
mermshaus

3
Không phù hợp với tôi cho một hình ảnh. Tôi đã CURLOPT_FOLLOWLOCATIONđặt thành true.
Nate

5
@Abenil thêm thông số này. curl_setopt ($ curl, CURLOPT_SSL_VERIFYPEER, false);
Davinder Kumar

1
@Davinder Kumar: cảm ơn rất nhiều, việc thêm mã của bạn để mã trên hoạt động.
Trung Lê Nguyễn Nhật

1
Chào mừng bạn! @TrungLeNguyenNhat
Davinder Kumar

31

Như đã đề cập một vài lần, con đường để đi là để lấy lại thông tin từ tiêu đề phản ứng của Content-Lengthlĩnh vực .

Tuy nhiên, bạn cần lưu ý rằng

  • máy chủ bạn đang thăm dò không nhất thiết phải triển khai phương thức HEAD (!)
  • hoàn toàn không cần phải tạo một yêu cầu HEAD theo cách thủ công (một lần nữa, thậm chí có thể không được hỗ trợ) bằng cách sử dụng fopenhoặc tương tự hoặc thậm chí để gọi thư viện curl, khi PHP có get_headers()(hãy nhớ: KISS )

Việc sử dụng get_headers()tuân theo nguyên tắc KISS hoạt động ngay cả khi máy chủ bạn đang thăm dò không hỗ trợ yêu cầu HEAD.

Vì vậy, đây là phiên bản của tôi (gimmick: trả về kích thước được định dạng có thể đọc được của con người ;-)):

Gist: https://gist.github.com/eyecatchup/f26300ffd7e50a92bc4d (phiên bản curl và get_headers)
get_headers () - Phiên bản:

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

Sử dụng:

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

Lưu ý thêm: Tiêu đề Nội dung-Độ dài là tùy chọn. Vì vậy, như một giải pháp chung, nó không phải là chống đạn !



2
Đây phải là câu trả lời được chấp nhận. Đúng, Content-Lengthlà tùy chọn, nhưng đó là cách duy nhất để có được kích thước tệp mà không cần tải xuống - và get_headerslà cách tốt nhất để có được content-length.
Quentin Skousen,

2
Hãy lưu ý rằng điều này sẽ thay đổi tùy chọn cho phương thức yêu cầu thành HEAD bên trong tất cả các yêu cầu HTTP tiếp theo cho quy trình PHP này. Sử dụng stream_context_createđể tạo ngữ cảnh riêng biệt để sử dụng cho cuộc gọi đến get_headers(7.1+).
MatsLindh

chỉ cần thêm, rằng nếu URL của bạn hoặc tên tập tin tài liệu đã khoảng trống trong nó, điều này sẽ trả về một -1
jasonflaherty

15

Chắc chắn rồi. Đưa ra yêu cầu chỉ dành cho Content-Lengthtiêu đề và tìm kiếm tiêu đề.


14

Hàm php hoạt get_headers()động để tôi kiểm tra độ dài nội dung như

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

Để biết thêm chi tiết: Hàm get_headers () trong PHP


4
Đối với tôi (với nginx) tiêu đề là Content-Length
Pangamma

7

Tôi không chắc, nhưng bạn không thể sử dụng hàm get_headers cho việc này?

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

Với ví dụ này, máy chủ đích tại $ url có thể khai thác get_headers để giữ cho kết nối mở cho đến khi quá trình PHP hết thời gian chờ (bằng cách trả về tiêu đề rất chậm, trong khi không đủ chậm để kết nối bị lỗi). Vì tổng số các quy trình PHP có thể bị giới hạn bởi FPM, điều này có thể cho phép một kiểu tấn công cu li chậm khi nhiều "người dùng" truy cập đồng thời vào tập lệnh get_headers của bạn.
Ted Phillips

6

một dòng giải pháp tốt nhất:

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

php quá ngon

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

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

3

Cách triển khai đơn giản và hiệu quả nhất:

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 cho biết "không tải tệp xuống." Phương pháp này tải tệp vào bộ nhớ từ máy chủ từ xa (ví dụ: tải xuống). Ngay cả với kết nối nhanh giữa các máy chủ, điều này có thể dễ dàng hết thời gian hoặc mất quá nhiều thời gian trên các tệp lớn. Lưu ý: Bạn chưa bao giờ đóng $ fp không thuộc phạm vi toàn cầu
Mavelo

1
Chức năng này KHÔNG tải nội dung càng lâu càng tốt; nếu nó chứa Content-Lengthtiêu đề. Và $fpviệc đóng cửa rõ ràng là KHÔNG CẦN THIẾT; nó được tự động phát hành khi hết hạn.php.net/manual/en/language.types.resource.php
mpyw

Bạn có thể dễ dàng xác nhận điều trên bằng cách sử dụngnc -l localhost 8080
mpyw

Trên thực tế, hầu hết các *closehàm là không cần thiết trong PHP hiện đại. Chúng xuất phát từ hai lý do lịch sử: hạn chế triển khai và bắt chước ngôn ngữ C.
mpyw

Tiêu đề không đáng tin cậy và tải xuống dự phòng đi ngược lại với OP. Cuối cùng, nếu bạn mở một tệp, chỉ cần đóng nó. Người thu gom rác không có lý do gì để các nhà phát triển lười biếng lưu một dòng mã.
Mavelo

2

Vì câu hỏi này đã được gắn thẻ "php" và "curl", tôi cho rằng bạn biết cách sử dụng Curl trong PHP.

Nếu bạn đặt curl_setopt(CURLOPT_NOBODY, TRUE)thì bạn sẽ đưa ra yêu cầu HEAD và có thể kiểm tra tiêu đề "Độ dài nội dung" của phản hồi, đây sẽ chỉ là tiêu đề.


2

Hãy thử chức năng dưới đây để nhận kích thước tệp từ xa

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

Đây là cái duy nhất làm việc cho tôi trên máy chủ apache Ubuntu Linux. Tôi đã phải init $ size và $ status khi bắt đầu chức năng, nếu không thì hoạt động như cũ.
Gavin Simpson

2

Đây là một cách tiếp cận khác sẽ hoạt động với các máy chủ không hỗ trợ HEAD yêu cầu.

Nó sử dụng cURL để đưa ra yêu cầu nội dung với tiêu đề phạm vi HTTP yêu cầu byte đầu tiên của tệp.

Nếu máy chủ hỗ trợ các yêu cầu phạm vi (hầu hết các máy chủ đa phương tiện) thì nó sẽ nhận được phản hồi với kích thước của tài nguyên.

Nếu máy chủ không phản hồi với một dải byte, nó sẽ tìm kiếm tiêu đề độ dài nội dung để xác định độ dài.

Nếu kích thước được tìm thấy trong tiêu đề dải ô hoặc độ dài nội dung, quá trình truyền sẽ bị hủy bỏ. Nếu kích thước không được tìm thấy và hàm bắt đầu đọc nội dung phản hồi, quá trình truyền sẽ bị hủy bỏ.

Đây có thể là một cách tiếp cận bổ sung nếu một HEADyêu cầu dẫn đến một 405phương pháp không được hỗ trợ phản hồi.

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

Sử dụng:

$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
Câu trả lời của bạn thực sự đã giúp tôi. Luôn trả lại câu trả lời. Ngay cả khi Content-Lengthkhông có sẵn.
Iman Hejazi

Xin chào, cảm ơn đã xem và bình luận. Tôi thực sự vui vì bạn thấy nó hữu ích!
vẽ010

1

Hầu hết các câu trả lời ở đây sử dụng CURL hoặc dựa trên tiêu đề đọc. Nhưng trong một số tình huống nhất định, bạn có thể sử dụng một cách giải quyết dễ dàng hơn. Xem xét ghi chú trên filesize()tài liệu của trên PHP.net . Bạn sẽ thấy có một mẹo nói rằng: " Kể từ PHP 5.0.0, chức năng này cũng có thể được sử dụng với một số trình bao bọc URL. Hãy tham khảo Giao thức và Trình bao bọc được hỗ trợ để xác định trình bao bọc nào hỗ trợ họ chức năng stat () ".

Vì vậy, nếu máy chủ và trình phân tích cú pháp PHP của bạn được định cấu hình đúng cách, bạn có thể chỉ cần sử dụng filesize()hàm, cung cấp cho nó với URL đầy đủ, trỏ đến một tệp từ xa, kích thước bạn muốn lấy và để PHP làm tất cả những điều kỳ diệu.


1

Hãy thử điều này: Tôi sử dụng nó và có kết quả tốt.

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

Bây giờ, hãy kiểm tra như sau:

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;

Các kết quả:

24,82 KiB

912 KiB

101,85 KiB


1

Để đáp ứng yêu cầu HTTP / 2, chức năng được cung cấp tại đây https://stackoverflow.com/a/2602624/2380767 cần được thay đổi một chút:

<?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;
}
?>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.