Làm cách nào để kiểm tra xem có tồn tại một tệp từ xa bằng PHP hay không?


86

Điều tốt nhất mà tôi có thể tìm thấy, một if fclose fopenđiều loại, làm cho trang tải thực sự chậm.

Về cơ bản những gì tôi đang cố gắng thực hiện như sau: Tôi có một danh sách các trang web và tôi muốn hiển thị các biểu tượng yêu thích của chúng bên cạnh chúng. Tuy nhiên, nếu một trang web không có, tôi muốn thay thế nó bằng một hình ảnh khác hơn là hiển thị một hình ảnh bị hỏng.


Tôi nghĩ bạn có thể sử dụng CURL và kiểm tra mã trả lại của nó. Nhưng nếu tốc độ là vấn đề, chỉ cần thực hiện ngoại tuyến và lưu vào bộ nhớ cache.
Michał Tatarynowicz

Có, nhưng tôi vẫn khuyên bạn nên sử dụng tập lệnh ngoại tuyến (chạy từ cron) phân tích cú pháp danh sách các trang web, kiểm tra xem chúng có biểu tượng yêu thích không và lưu dữ liệu đó vào bộ nhớ cache cho giao diện người dùng. Nếu bạn không / không thể sử dụng cron, ít nhất hãy lưu kết quả vào bộ nhớ cache cho mọi URL mới mà bạn kiểm tra.
Michał Tatarynowicz

3
Để thay thế một hình ảnh bị phá vỡ với một hình ảnh giữ chỗ trong trình duyệt, vui lòng xem xét một giải pháp client-side sử dụng onerrorhình ảnh ví dụ như một giải pháp sử dụng jQuery

Câu trả lời:


135

Bạn có thể hướng dẫn curl sử dụng phương thức HTTP HEAD qua CURLOPT_NOBODY.

Nhiều hơn hoặc ít hơn

$ch = curl_init("http://www.example.com/favicon.ico");

curl_setopt($ch, CURLOPT_NOBODY, true);
curl_exec($ch);
$retcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// $retcode >= 400 -> not found, $retcode = 200, found.
curl_close($ch);

Dù sao, bạn chỉ tiết kiệm chi phí truyền HTTP chứ không phải thiết lập và đóng kết nối TCP. Và các biểu tượng yêu thích nhỏ, bạn có thể không thấy nhiều cải thiện.

Lưu vào bộ nhớ cache cục bộ kết quả có vẻ là một ý tưởng hay nếu nó quá chậm. HEAD kiểm tra thời gian của tệp và trả lại tệp trong tiêu đề. Bạn có thể làm như các trình duyệt và nhận CURLINFO_FILETIME của biểu tượng. Trong bộ nhớ cache, bạn có thể lưu trữ URL => [favicon, timestamp]. Sau đó, bạn có thể so sánh dấu thời gian và tải lại biểu tượng yêu thích.


6
chỉ là một lưu ý: retcodelỗi trên tất cả 400 mã để xác nhận sẽ >=không chỉ>
Justin Bull

4
Một số trang web chặn truy cập nếu bạn không cung cấp một chuỗi tác nhân người dùng, vì vậy tôi khuyên bạn nên làm theo hướng dẫn này để thêm CURLOPT_USERAGENT ngoài CURLOPT_NOBODY: davidwalsh.name/set-user-agent-php-curl-spoof
rlorenzo

6
Mã sửa lại @Lyth 3XX không phải là một lỗi mà là một sự chuyển hướng. Chúng phải được xử lý thủ công hoặc sử dụng CURLOPT_FOLLOWLOCATION.
Ramon Poca

6
Sử dụng curl_setopt ($ ch, CURLOPT_SSL_VERIFYPEER, false); cũng như để đảm bảo mã tương tự hoạt động cho URL bắt đầu bằng HTTPS!
Krishan Gopal

61

Như Pies nói rằng bạn có thể sử dụng cURL. Bạn có thể nhận cURL để chỉ cung cấp cho bạn phần tiêu đề chứ không phải phần nội dung, điều này có thể làm cho nó nhanh hơn. Một miền không hợp lệ luôn có thể mất một lúc vì bạn sẽ chờ yêu cầu hết thời gian chờ; bạn có thể thay đổi thời lượng chờ bằng cURL.

Đây là ví dụ:

function remoteFileExists($url) {
    $curl = curl_init($url);

    //don't fetch the actual page, you only want to check the connection is ok
    curl_setopt($curl, CURLOPT_NOBODY, true);

    //do request
    $result = curl_exec($curl);

    $ret = false;

    //if request did not fail
    if ($result !== false) {
        //if request was ok, check response code
        $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);  

        if ($statusCode == 200) {
            $ret = true;   
        }
    }

    curl_close($curl);

    return $ret;
}

$exists = remoteFileExists('http://stackoverflow.com/favicon.ico');
if ($exists) {
    echo 'file exists';
} else {
    echo 'file does not exist';   
}

3
remoteFileExists (' stackoverflow.com/' ) điều này cũng sẽ trả về true, nhưng nó chỉ là một liên kết. Chức năng này không kiểm tra nội dung liên kết là loại tệp.
Donatas Navidonskis

36

Giải pháp của CoolGoose là tốt nhưng điều này nhanh hơn đối với các tệp lớn (vì nó chỉ cố gắng đọc 1 byte):

if (false === file_get_contents("http://example.com/path/to/image",0,null,0,1)) {
    $image = $default_image;
}

+1. Có những hạn chế nào cho giải pháp này so với giải pháp CURL không?
Adriano Varoli Piazza

1
bạn chỉ có thể sử dụng fopen- nếu mã trả về yêu cầu là 404, fopen trả về false.
s3v3n

này là rất chậm và không làm việc cho tôi (nghĩa là nó vẫn hiển thị một hình ảnh bị phá vỡ nếu đường dẫn tập tin là không đúng)
Helmut

Cách tiếp cận này không hoạt động nếu máy chủ thực hiện chuyển hướng bất cứ khi nào hình ảnh hoặc tệp không tồn tại. Điều này xảy ra khi một trang web sử dụng mod_rewrite hoặc một số loại "quy tắc" khác để xử lý các yêu cầu.
Erik Čerpnjak

28

Đây không phải là câu trả lời cho câu hỏi ban đầu của bạn, mà là một cách tốt hơn để thực hiện những gì bạn đang cố gắng làm:

Thay vì thực sự cố gắng tải trực tiếp biểu tượng yêu thích của trang web (đó là nỗi đau của hoàng gia vì nó có thể là /favicon.png, /favicon.ico, /favicon.gif hoặc thậm chí /path/to/favicon.png), hãy sử dụng google:

<img src="http://www.google.com/s2/favicons?domain=[domain]">

Làm xong.


4
Cú pháp làm cho một chút nhầm lẫn. Vì vậy, đây là một ví dụ: <img src = " google.com/s2/favicons?domain=stackoverflow.com ">
Habeeb Perwad

19

Một chức năng hoàn chỉnh của câu trả lời được bình chọn nhiều nhất:

function remote_file_exists($url)
{
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_NOBODY, 1);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); # handles 301/2 redirects
    curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    if( $httpCode == 200 ){return true;}
}

Bạn có thể sử dụng nó như thế này:

if(remote_file_exists($url))
{
    //file exists, do something
}

Oh! Tôi đã đi vắng vài ngày gần đây nhưng đầu tháng gần như 24/7. Cảm ơn bạn đã cho tôi biết!
Pedro Lobito

Điều này không hoạt động nếu máy chủ không phản hồi bất kỳ mã HTTP nào (hoặc cUrl không bắt được nó). Điều đó đang xảy ra với tôi khá thường xuyên. Ví dụ. trong trường hợp hình ảnh.
Vaci

điều gì sẽ xảy ra nếu url được chuyển hướng đến một URL hoặc phiên bản https khác? Trong trường hợp đó, mã cuộn này sẽ không thể thực hiện công việc. cách tốt nhất là lấy thông tin tiêu đề và tìm kiếm chuỗi không phân biệt chữ hoa chữ thường "200 ok".
Infoconic

@Infoconic Bạn có thể thêm curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);. Tôi đã cập nhật câu trả lời để xử lý 302chuyển hướng.
Pedro Lobito

18

Nếu bạn đang xử lý hình ảnh, hãy sử dụng kích thước địa lý. Không giống như file_exists, chức năng tích hợp này hỗ trợ các tệp từ xa. Nó sẽ trả về một mảng chứa thông tin hình ảnh (chiều rộng, chiều cao, loại..vv). Tất cả những gì bạn phải làm là kiểm tra phần tử đầu tiên trong mảng (chiều rộng). sử dụng print_r để xuất nội dung của mảng

$imageArray = getimagesize("http://www.example.com/image.jpg");
if($imageArray[0])
{
    echo "it's an image and here is the image's info<br>";
    print_r($imageArray);
}
else
{
    echo "invalid image";
}

Kết quả là cảnh báo 404 khi tài nguyên từ xa không khả dụng. Hiện tại, tôi đã xử lý nó bằng cách ngăn chặn lỗi sử dụng @phía trước getimagesize, nhưng cảm thấy tội lỗi cho vụ hack này.

Trong trường hợp của tôi, đây là cách tiếp cận tốt nhất, bởi vì tôi được chuyển hướng bất cứ khi nào hình ảnh / tệp không tồn tại. Tôi thứ hai rằng lỗi ngăn chặn với @ là không nên nhưng trong trường hợp này, nó là cần thiết.
Erik Čerpnjak

Tôi đã tìm ra rằng chúng ta cũng có thể sử dụng exif_imagetype, và nó nhanh hơn nhiều stackoverflow.com/a/38295345/1250044
yckart

7

Điều này có thể được thực hiện bằng cách lấy mã Trạng thái HTTP (404 = not found), mã này có thể thực hiện được với file_get_contentsTài liệu sử dụng các tùy chọn ngữ cảnh. Đoạn mã sau sẽ tính đến các chuyển hướng và sẽ trả về mã trạng thái của điểm đến cuối cùng ( Demo ):

$url = 'http://example.com/';
$code = FALSE;

$options['http'] = array(
    'method' => "HEAD",
    'ignore_errors' => 1
);

$body = file_get_contents($url, NULL, stream_context_create($options));

foreach($http_response_header as $header)
    sscanf($header, 'HTTP/%*d.%*d %d', $code);

echo "Status code: $code";

Nếu bạn không muốn theo dõi các chuyển hướng, bạn có thể thực hiện tương tự ( Demo ):

$url = 'http://example.com/';
$code = FALSE;

$options['http'] = array(
    'method' => "HEAD",
    'ignore_errors' => 1,
    'max_redirects' => 0
);

$body = file_get_contents($url, NULL, stream_context_create($options));

sscanf($http_response_header[0], 'HTTP/%*d.%*d %d', $code);

echo "Status code: $code";

Một số hàm, tùy chọn và biến đang sử dụng được giải thích chi tiết hơn trong một bài đăng trên blog mà tôi đã viết: HEAD trước với PHP Streams .




Để biết thêm về PHP, $http_response_headerhãy xem php.net/manual/en/reserved.variables.httpresponseheader.php .
Big McLargeHuge

1
Biến thể thứ hai phù hợp với tôi và so với lệnh gọi file_get_contents mặc định (không có stream_context tùy chỉnh), nó nhanh hơn 50%, tức là từ 3,4 giây đến 1,7 giây cho một yêu cầu.
Erik Čerpnjak

@ ErikČerpnjak: Nếu không có stream_context "không tùy chỉnh", thì đó là dòng mặc định. Bạn có thể nhận các tùy chọn từ ngữ cảnh mặc định và xem chúng khác nhau như thế nào so với ngữ cảnh tùy chỉnh của bạn. Điều này sẽ cung cấp cho bạn một số thông tin chi tiết tại sao thời gian lại khác nhau. - php.net/stream-context-get-defaultphp.net/stream-context-get-options
hakre

6
if (false === file_get_contents("http://example.com/path/to/image")) {
    $image = $default_image;
}

Nên làm việc ;)


thêm @ trước hàm
Tebe

6

Các chức năng có sẵn của PHP có thể không hoạt động để kiểm tra URL nếu cài đặt allow_url_fopen được đặt thành tắt vì lý do bảo mật. Curl là một lựa chọn tốt hơn vì chúng tôi sẽ không cần phải thay đổi mã của mình ở giai đoạn sau. Dưới đây là mã tôi đã sử dụng để xác minh một URL hợp lệ:

$url = str_replace(' ', '%20', $url);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);  
curl_close($ch);
if($httpcode>=200 && $httpcode<300){  return true; } else { return false; } 

Vui lòng lưu ý tùy chọn CURLOPT_SSL_VERIFYPEER cũng xác minh URL bắt đầu bằng HTTPS.


6

Để kiểm tra sự tồn tại của hình ảnh, exif_imagetypenên được ưu tiên hơngetimagesize , vì nó nhanh hơn nhiều.

Để loại bỏ E_NOTICE, chỉ cần thêm toán tử kiểm soát lỗi ( @).

if (@exif_imagetype($filename)) {
  // Image exist
}

Như một phần thưởng, với giá trị trả về ( IMAGETYPE_XXX) từ exif_imagetypechúng tôi cũng có thể nhận được kiểu mime hoặc phần mở rộng tệp với image_type_to_mime_type/ image_type_to_extension.


4

Một giải pháp triệt để là hiển thị các biểu tượng yêu thích dưới dạng hình nền trong một div phía trên biểu tượng mặc định của bạn. Bằng cách đó, tất cả chi phí sẽ được đặt trên máy khách trong khi vẫn không hiển thị hình ảnh bị hỏng (hình ảnh nền bị thiếu bị bỏ qua trong tất cả các trình duyệt AFAIK).


1
+1 nếu bạn không kiểm tra nhiều vị trí cho biểu tượng yêu thích của họ (favicon.ico, favicon.gif, favicon.png) thì đây có vẻ là giải pháp tốt nhất
Galen

3
function remote_file_exists($url){
   return(bool)preg_match('~HTTP/1\.\d\s+200\s+OK~', @current(get_headers($url)));
}  
$ff = "http://www.emeditor.com/pub/emed32_11.0.5.exe";
    if(remote_file_exists($ff)){
        echo "file exist!";
    }
    else{
        echo "file not exist!!!";
    }

3

Bạn có thể sử dụng như sau:

$file = 'http://mysite.co.za/images/favicon.ico';
$file_exists = (@fopen($file, "r")) ? true : false;

Đã làm việc cho tôi khi cố gắng kiểm tra xem hình ảnh có tồn tại trên URL hay không


2

Bạn có thể dùng :

$url=getimagesize(“http://www.flickr.com/photos/27505599@N07/2564389539/”);

if(!is_array($url))
{
   $default_image =”…/directoryFolder/junal.jpg”;
}

2

Điều này hoạt động để tôi kiểm tra xem tệp từ xa có tồn tại trong PHP hay không:

$url = 'https://cdn.sstatic.net/Sites/stackoverflow/img/favicon.ico';
    $header_response = get_headers($url, 1);

    if ( strpos( $header_response[0], "404" ) !== false ) {
        echo 'File does NOT exist';
        } else {
        echo 'File exists';
        }


1

Có một sự thay thế thậm chí còn phức tạp hơn. Bạn có thể thực hiện kiểm tra tất cả phía máy khách bằng thủ thuật JQuery.

$('a[href^="http://"]').filter(function(){
     return this.hostname && this.hostname !== location.hostname;
}).each(function() {
    var link = jQuery(this);
    var faviconURL =
      link.attr('href').replace(/^(http:\/\/[^\/]+).*$/, '$1')+'/favicon.ico';
    var faviconIMG = jQuery('<img src="favicon.png" alt="" />')['appendTo'](link);
    var extImg = new Image();
    extImg.src = faviconURL;
    if (extImg.complete)
      faviconIMG.attr('src', faviconURL);
    else
      extImg.onload = function() { faviconIMG.attr('src', faviconURL); };
});

Từ http://snipplr.com/view/18782/add-a-favicon-near-external-links-with-jquery/ (blog gốc hiện đã ngừng hoạt động)


1

tất cả các câu trả lời ở đây sử dụng get_headers () đang thực hiện một yêu cầu GET. Nhanh hơn / rẻ hơn nhiều khi chỉ thực hiện một yêu cầu HEAD.

Để đảm bảo rằng get_headers () thực hiện yêu cầu HEAD thay vì GET, bạn nên thêm điều này:

stream_context_set_default(
    array(
        'http' => array(
            'method' => 'HEAD'
        )
    )
);

vì vậy để kiểm tra xem tệp có tồn tại hay không, mã của bạn sẽ trông giống như sau:

stream_context_set_default(
    array(
        'http' => array(
            'method' => 'HEAD'
        )
    )
);
$headers = get_headers('http://website.com/dir/file.jpg', 1);
$file_found = stristr($headers[0], '200');

$ file_found hiển nhiên sẽ trả về false hoặc true.


0

Không biết cái này có nhanh hơn không khi tệp không tồn tại từ xa, is_file () , nhưng bạn có thể thử.

$favIcon = 'default FavIcon';
if(is_file($remotePath)) {
   $favIcon = file_get_contents($remotePath);
}

Từ tài liệu: "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 được hỗ trợ và Trình bao bọc để xác định trình bao bọc nào hỗ trợ họ chức năng stat ()."
PatrikAkerstrand

Ý bạn là điều này có thể hoạt động nếu bạn đăng ký một trình bao bọc luồng? Chỉnh sửa câu hỏi của bạn để hiển thị một ví dụ hoạt động và tôi sẽ xóa phiếu phản đối của mình (và ủng hộ bạn nếu tôi có thể). Nhưng hiện tại, tôi đã kiểm tra is_file từ cli php bằng một tệp từ xa và nhận được sai.
greg0ire,

Ví dụ không làm việc:var_dump(is_file('http://cdn.sstatic.net/stackoverflow/img/sprites.png')); bool(false)
greg0ire

0

Nếu tệp không được lưu trữ bên ngoài, bạn có thể dịch URL từ xa thành Đường dẫn tuyệt đối trên máy chủ web của mình. Bằng cách đó, bạn không phải gọi CURL hoặc file_get_contents, v.v.

function remoteFileExists($url) {

    $root = realpath($_SERVER["DOCUMENT_ROOT"]);
    $urlParts = parse_url( $url );

    if ( !isset( $urlParts['path'] ) )
        return false;

    if ( is_file( $root . $urlParts['path'] ) )
        return true;
    else
        return false;

}

remoteFileExists( 'https://www.yourdomain.com/path/to/remote/image.png' );

Lưu ý: Máy chủ web của bạn phải điền DOCUMENT_ROOT để sử dụng chức năng này


0

Nếu bạn đang sử dụng khung công tác Symfony, cũng có một cách đơn giản hơn nhiều bằng cách sử dụng HttpClientInterface:

private function remoteFileExists(string $url, HttpClientInterface $client): bool {
    $response = $client->request(
        'GET',
        $url //e.g. http://example.com/file.txt
    );

    return $response->getStatusCode() == 200;
}

Các tài liệu cho HttpClient cũng rất tốt và có thể đáng xem xét nếu bạn cần một cách tiếp cận cụ thể hơn: https://symfony.com/doc/current/http_client.html


-1

Bạn có thể sử dụng hệ thống tệp: sử dụng Symfony \ Component \ Filesystem \ Filesystem; sử dụng Symfony \ Component \ Filesystem \ Exception \ IOExceptionInterface;

và kiểm tra $ fileSystem = new Filesystem (); if ($ fileSystem-> being ('path_to_file') == true) {...

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.