파일 다운로드없이 원격 파일 크기


답변:


100

여기 에 대해 뭔가를 찾았습니다 .

원격 파일의 크기를 가져 오는 가장 좋은 방법은 다음과 같습니다. HEAD 요청은 요청의 실제 본문을 가져 오지 않고 헤더 만 검색합니다. 따라서 100MB의 리소스에 대한 HEAD 요청을 만드는 것은 1KB의 리소스에 대한 HEAD 요청과 동일한 시간이 걸립니다.

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

용법:

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

4
그러나 Content-length없이 응답 이있을 있음 을 명심하십시오 .
VolkerK 2010

4
curl_getinfo@macki가 제안한 것처럼 사용하는 것이 더 낫지 않습니까?
Svish

1
@Svish, 예, 그 접근 방식이 실제로 작동하기 때문입니다. 여기에 제시된 접근 방식은 최종 Content-Length 가 아닌 첫 번째 Content-Length를 가져 오기 때문에 리디렉션 된 URL에서 실패합니다 . 내 경험상.
Bobby Jack

12
이것은 get_user_agent_string()정의되지 않은 것처럼 나를 위해 작동하지 않았습니다 . 전체 라인을 제거하면 모든 것이 작동했습니다.
Rapti

1
테스트 할 때 실패합니다 : http://www.dailymotion.com/rss/user/dialhainaut/SO : stackoverflow.com/questions/36761377/…
ErickBest

63

이 코드 시도

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

이 방법이 작동하지 않으면 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);.
mermshaus

3
이미지에 대해서는 작동하지 않습니다. 나는 CURLOPT_FOLLOWLOCATION사실로 설정했습니다.
Nate

5
@Abenil은이 매개 변수를 추가합니다. curl_setopt ($ curl, CURLOPT_SSL_VERIFYPEER, false);
Davinder 쿠마

1
@Davinder Kumar : 감사합니다. 코드를 추가하면 위 코드가 작동합니다.
트룽 르 구엔 나트

1
천만에요! @TrungLeNguyenNhat
Davinder 쿠마

31

몇 번 언급했듯이 이동 방법 은 응답 헤더의 Content-Length필드 에서 정보를 검색하는 것 입니다 .

그러나

  • 검색중인 서버가 반드시 HEAD 메서드 (!)를 구현하는 것은 아닙니다.
  • fopenPHP가있는 경우 get_headers()(기억 : KISS ) or alike를 사용하거나 심지어 curl 라이브러리를 호출 하기 위해 수동으로 HEAD 요청 (다시 말하지만 지원되지 않을 수도 있음)을 만들 필요가 없습니다 .

의 사용은 get_headers()다음 KISS 원칙 프로빙하고있는 서버가 HEAD 요청을 지원하지 않는 경우에도 작동합니다.

그래서, 여기 내 버전이 있습니다 (gimmick : 사람이 읽을 수있는 형식의 크기를 반환합니다 ;-)) :

요점 : https://gist.github.com/eyecatchup/f26300ffd7e50a92bc4d(curl 및 get_headers 버전)
get_headers ()-버전 :

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

용법:

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

추가 참고 사항 : Content-Length 헤더는 선택 사항입니다. 따라서 일반적인 솔루션으로 방탄이 아닙니다 !



2
이것은 받아 들여진 대답이어야합니다. 사실, Content-Length선택 사항이지만, 그것을 다운로드하지 않고 파일 크기를 얻을 수있는 유일한 방법입니다 - 그리고 get_headers얻을 수있는 가장 좋은 방법입니다 content-length.
Quentin Skousen

2
이렇게하면이 PHP 프로세스에 대한 모든 후속 HTTP 요청 내에서 요청 메서드의 기본 설정이 HEAD로 변경됩니다. 사용 stream_context_create에 대한 호출에 사용하는 별도의 컨텍스트를 만드는 get_headers(7.1 이상).
MatsLindh

당신의 URL 또는 문서의 파일 이름에 공백이 있다면이 -1을 돌려 보낼 추가
jasonflaherty


14

PHP 함수는 get_headers()수표의 나를 위해 작동 내용 길이를

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

자세한 내용은 PHP 함수 get_headers ()


4
(의 nginx와) 나를 위해 헤더 콘텐츠 길이였다
Pangamma

7

잘 모르겠지만 get_headers 함수를 사용할 수 없습니까?

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

이 예제를 사용하면 $ url의 대상 서버가 get_headers를 이용하여 PHP 프로세스가 시간 초과 될 때까지 연결을 유지하도록 할 수 있습니다 (연결이 오래되지 않도록 헤더를 매우 느리게 반환함으로써). 전체 PHP 프로세스는 FPM에 의해 제한 될 수 있으므로 여러 "사용자"가 get_headers 스크립트에 동시에 액세스 할 때 일종의 느린 loris 공격을 허용 할 수 있습니다.
Ted Phillips

6

한 줄 최고의 솔루션 :

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

PHP는 너무 섬세합니다

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

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

3

가장 간단하고 효율적인 구현 :

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는 "파일을 다운로드하지 않고"라고 표시했습니다. 이 방법은 원격 서버에서 메모리로 파일을로드합니다 (예 : 다운로드). 서버 간의 빠른 연결이 있더라도 이는 쉽게 시간 초과되거나 대용량 파일에서 너무 오래 걸릴 수 있습니다. 참고 : 글로벌 범위에 포함되지 인 $ FP 폐쇄 결코
Mavelo

1
이 기능은 가능한 한 오랫동안 본문을 다운로드하지 않습니다. Content-Length헤더 가 포함 된 경우 . 그리고 명시적인 $fp종료는 필요하지 않습니다. 만료시 자동으로 해제됩니다. php.net/manual/en/language.types.resource.php
mpyw

위의 내용을 쉽게 확인할 수 있습니다.nc -l localhost 8080
mpyw

사실 *close최신 PHP 에서는 대부분의 기능이 필요하지 않습니다. 두 가지 역사적 이유, 즉 구현 제한과 C 언어 모방 때문입니다.
mpyw

헤더는 신뢰할 수 없으며 대체 다운로드가 OP에 반대됩니다. 마지막으로 파일을 열면 닫으십시오. 가비지 수집기는 한 줄의 코드를 저장하는 게으른 개발자에게 변명의 여지가 없습니다.
Mavelo

2

이 질문은 이미 "php"및 "curl"태그가 지정되어 있으므로 PHP에서 Curl을 사용하는 방법을 알고 있다고 가정합니다.

설정 curl_setopt(CURLOPT_NOBODY, TRUE)하면 HEAD 요청을하고 응답의 "Content-Length"헤더를 확인할 수 있습니다. 이는 헤더뿐입니다.


2

원격 파일 크기를 얻으려면 아래 기능을 시도하십시오.

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 서버에서 나를 위해 일한 유일한 것입니다. 함수 시작시 $ size와 $ status를 초기화해야했고, 그렇지 않으면 그대로 작동했습니다.
Gavin Simpson

2

다음은 HEAD요청을 지원하지 않는 서버에서 작동하는 또 다른 접근 방식입니다 .

cURL을 사용하여 파일의 첫 번째 바이트를 요청하는 HTTP 범위 헤더가있는 콘텐츠를 요청합니다.

서버가 범위 요청을 지원하는 경우 (대부분의 미디어 서버에서 지원) 리소스 크기에 대한 응답을받습니다.

서버가 바이트 범위로 응답하지 않으면 콘텐츠 길이 헤더를 찾아 길이를 결정합니다.

범위 또는 콘텐츠 길이 헤더에서 크기가 발견되면 전송이 중단됩니다. 크기를 찾을 수없고 함수가 응답 본문 읽기를 시작하면 전송이 중단됩니다.

HEAD요청으로 인해 405메서드가 지원되지 않는 응답이 발생하는 경우 이는 추가 접근 방식이 될 수 있습니다 .

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

용법:

$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
귀하의 답변은 정말 도움이되었습니다. 항상 답을 반환합니다. Content-Length사용할 수없는 경우에도 .
Iman Hejazi 2010 년

안녕하세요, 찾아 주셔서 감사합니다. 도움이되었다 니 정말 다행입니다!
drew010

1

여기에서 대부분의 답변은 CURL을 사용하거나 헤더 읽기를 기반으로합니다. 그러나 일부 특정 상황에서는 더 쉬운 솔루션을 사용할 수 있습니다. filesize()PHP.net의 문서를 참고하십시오 . " PHP 5.0.0부터이 함수는 일부 URL 래퍼와 함께 사용할 수도 있습니다. stat () 기능 군을 지원하는 래퍼를 확인 하려면 지원되는 프로토콜 및 래퍼 를 참조하세요. "라는 팁 이 있습니다 .

따라서 서버와 PHP 파서가 올바르게 구성되어 있으면 filesize()함수 를 사용 하고 전체 URL을 제공하고 원하는 크기의 원격 파일을 가리키고 PHP가 모든 마법을 수행하도록 할 수 있습니다.


1

이것을 시도하십시오 : 나는 그것을 사용하고 좋은 결과를 얻었습니다.

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

이제 다음과 같이 테스트하십시오.

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;

결과 :

24.82KiB

912KiB

101.85 KiB


1

HTTP / 2 요청을 처리하려면 https://stackoverflow.com/a/2602624/2380767에 제공된 기능을 약간 변경해야합니다.

<?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;
}
?>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.