จะตรวจสอบได้อย่างไรว่ามีไฟล์ระยะไกลโดยใช้ PHP หรือไม่?


87

สิ่งที่ดีที่สุดที่ฉันสามารถหาได้คือif fclose fopenสิ่งที่ทำให้หน้าเว็บโหลดช้ามาก

โดยทั่วไปสิ่งที่ฉันพยายามทำมีดังต่อไปนี้: ฉันมีรายชื่อเว็บไซต์และฉันต้องการแสดง Favicons ของพวกเขาข้างๆ อย่างไรก็ตามหากไซต์ไม่มีฉันต้องการแทนที่ด้วยรูปภาพอื่นแทนการแสดงรูปภาพที่เสียหาย


ฉันคิดว่าคุณสามารถใช้ CURL และตรวจสอบรหัสส่งคืนได้ แต่ถ้าเป็นความเร็วที่เป็นปัญหาก็ทำแบบออฟไลน์และแคช
Michał Tatarynowicz

ใช่ แต่ฉันยังคงแนะนำให้ใช้สคริปต์ออฟไลน์ (เรียกใช้จาก cron) ที่แยกวิเคราะห์รายชื่อเว็บไซต์ตรวจสอบว่าพวกเขามี Favicons และแคชข้อมูลนั้นสำหรับส่วนหน้าหรือไม่ หากคุณไม่ / ไม่สามารถใช้ cron ได้อย่างน้อยแคชผลลัพธ์สำหรับทุก URL ใหม่ที่คุณตรวจสอบ
Michał Tatarynowicz

3
สำหรับการแทนที่รูปภาพที่เสียด้วยภาพตัวยึดในเบราว์เซอร์โปรดพิจารณาโซลูชันฝั่งไคลเอ็นต์โดยใช้onerrorรูปภาพเช่นโซลูชันที่ใช้ jQuery

คำตอบ:


136

คุณสามารถสั่งให้ curl ใช้เมธอด HTTP HEAD ผ่าน CURLOPT_NOBODY

มากหรือน้อย

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

อย่างไรก็ตามคุณประหยัดค่าใช้จ่ายในการถ่ายโอน HTTP เท่านั้นไม่ใช่การสร้างและปิดการเชื่อมต่อ TCP และเนื่องจาก Favicons มีขนาดเล็กคุณอาจไม่เห็นการปรับปรุงมากนัก

การแคชผลลัพธ์ในเครื่องดูเหมือนเป็นความคิดที่ดีหากปรากฎว่าช้าเกินไป HEAD ตรวจสอบเวลาของไฟล์และส่งคืนในส่วนหัว คุณสามารถทำเหมือนเบราว์เซอร์และรับ CURLINFO_FILETIME ของไอคอน ในแคชของคุณคุณสามารถจัดเก็บ URL => [favicon, timestamp] จากนั้นคุณสามารถเปรียบเทียบการประทับเวลาและโหลดไอคอน Fav ซ้ำได้


6
หมายเหตุ: retcodeข้อผิดพลาดในรหัสทั้งหมด 400 รหัสดังนั้นการตรวจสอบจะ>=ไม่ใช่แค่>
Justin Bull

4
บางไซต์บล็อกการเข้าถึงหากคุณไม่ได้ระบุสตริงตัวแทนผู้ใช้ดังนั้นฉันขอแนะนำให้ทำตามคำแนะนำนี้เพื่อเพิ่ม CURLOPT_USERAGENT นอกเหนือจาก CURLOPT_NOBODY: davidwalsh.name/set-user-agent-php-curl-spoof
rlorenzo

6
@Lyth 3XX รหัสใหม่ไม่ใช่ข้อผิดพลาด แต่เป็นการเปลี่ยนเส้นทาง ควรจัดการด้วยตนเองหรือใช้ CURLOPT_FOLLOWLOCATION
Ramon Poca

6
ใช้ curl_setopt ($ ch, CURLOPT_SSL_VERIFYPEER, false); เช่นกันเพื่อให้แน่ใจว่ารหัสเดียวกันใช้ได้กับ URL ที่ขึ้นต้นด้วย HTTPS!
Krishan Gopal

61

อย่างที่พายบอกว่าใช้ cURL ได้ คุณสามารถรับ cURL เพื่อให้เฉพาะส่วนหัวเท่านั้นไม่ใช่ส่วนหัวซึ่งอาจทำให้เร็วขึ้น โดเมนที่ไม่ดีอาจใช้เวลาสักครู่เนื่องจากคุณจะรอให้คำขอหมดเวลา คุณอาจเปลี่ยนระยะหมดเวลาโดยใช้ cURL

นี่คือตัวอย่าง:

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/' ) สิ่งนี้จะคืนค่าจริง แต่เป็นเพียงลิงก์ ฟังก์ชันนี้ไม่ได้ตรวจสอบคือประเภทเนื้อหาลิงก์เป็นไฟล์
Donatas Navidonskis

36

วิธีแก้ปัญหาของ CoolGoose นั้นดี แต่จะเร็วกว่าสำหรับไฟล์ขนาดใหญ่ (เนื่องจากพยายามอ่าน 1 ไบต์เท่านั้น):

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

+1. มีข้อเสียอะไรบ้างสำหรับโซลูชันนี้กับ CURL?
Adriano Varoli Piazza

1
คุณสามารถใช้ได้fopen- ถ้ารหัสส่งคืนคำขอคือ 404 fopen จะส่งคืนเท็จ
s3v3n

สิ่งนี้ช้ามากและไม่ได้ผลสำหรับฉัน (หมายความว่ายังแสดงภาพที่เสียหายหากเส้นทางของไฟล์ไม่ถูกต้อง)
Helmut

วิธีนี้ใช้ไม่ได้หากเซิร์ฟเวอร์ทำการเปลี่ยนเส้นทางเมื่อใดก็ตามที่ไม่มีรูปภาพหรือไฟล์ เหตุการณ์นี้เกิดขึ้นเมื่อไซต์ใช้ mod_rewrite หรือ "กฎ" ประเภทอื่น ๆ ว่าควรจัดการคำขออย่างไร
Erik Čerpnjak

28

นี่ไม่ใช่คำตอบสำหรับคำถามเดิมของคุณ แต่เป็นวิธีที่ดีกว่าในการทำสิ่งที่คุณพยายามทำ:

แทนที่จะพยายามรับ Favicon ของไซต์โดยตรง (ซึ่งเป็นความเจ็บปวดของราชวงศ์เนื่องจากอาจเป็น /favicon.png, /favicon.ico, /favicon.gif หรือแม้แต่ /path/to/favicon.png) ให้ใช้ google:

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

เสร็จแล้ว


4
ไวยากรณ์ทำให้สับสนเล็กน้อย นี่คือตัวอย่างหนึ่ง: <img src = " google.com/s2/favicons?domain=stackoverflow.com ">
Habeeb Perwad

19

ฟังก์ชั่นที่สมบูรณ์ของคำตอบที่ได้รับการโหวตมากที่สุด:

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

คุณสามารถใช้มันได้ดังนี้:

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

โอ้! ฉันไม่อยู่ในช่วงสองสามวันที่ผ่านมา แต่ต้นเดือนเกือบ 24/7 ขอบคุณที่ทำให้ฉันรู้!
Pedro Lobito

สิ่งนี้จะใช้ไม่ได้หากเซิร์ฟเวอร์ไม่ตอบสนองรหัส HTTP ใด ๆ (หรือ cUrl ไม่จับมัน) ซึ่งค่อนข้างบ่อยสำหรับฉัน เช่น. ในกรณีของภาพ
Vaci

จะเกิดอะไรขึ้นถ้า url ถูกเปลี่ยนเส้นทางไปยัง URL อื่นหรือเวอร์ชัน https? ในกรณีนี้รหัส curl นี้จะไม่สามารถทำงานได้ วิธีที่ดีที่สุดคือรับข้อมูลส่วนหัวและค้นหาสตริงที่ไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ "200 ok"
Infoconic

@Infoconic curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);คุณสามารถเพิ่ม ฉันได้อัปเดตคำตอบเพื่อจัดการการ302เปลี่ยนเส้นทางแล้ว
Pedro Lobito

18

หากคุณกำลังจัดการกับรูปภาพให้ใช้ getimagesize ซึ่งแตกต่างจาก file_exists ฟังก์ชันในตัวนี้รองรับไฟล์ระยะไกล มันจะส่งคืนอาร์เรย์ที่มีข้อมูลรูปภาพ (ความกว้างความสูงประเภท .. ฯลฯ ) สิ่งที่คุณต้องทำคือตรวจสอบองค์ประกอบแรกในอาร์เรย์ (ความกว้าง) ใช้ print_r เพื่อส่งออกเนื้อหาของอาร์เรย์

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

ผลลัพธ์เป็นคำเตือน 404 เมื่อทรัพยากรระยะไกลไม่พร้อมใช้งาน ในขณะนี้ฉันจัดการกับมันโดยการระงับข้อผิดพลาดโดยใช้@ต่อหน้าgetimagesizeแต่รู้สึกผิดต่อการแฮ็คนี้

ในกรณีของฉันนี่เป็นแนวทางที่ดีที่สุดเพราะฉันจะถูกเปลี่ยนเส้นทางเมื่อใดก็ตามที่ไม่มีรูปภาพ / ไฟล์ ฉันสองว่าข้อผิดพลาดในการระงับด้วย @ นั้นไม่ต้องไป แต่ในกรณีนี้จำเป็น
Erik Čerpnjak

ฉันคิดว่าเราสามารถใช้ได้exif_imagetypeและมันเร็วกว่ามากstackoverflow.com/a/38295345/1250044
yckart

7

สามารถทำได้โดยการรับรหัสสถานะ HTTP (404 = ไม่พบ) ซึ่งเป็นไปได้ที่file_get_contentsเอกสารจะใช้ตัวเลือกบริบท รหัสต่อไปนี้จะพิจารณาการเปลี่ยนเส้นทางและจะส่งคืนรหัสสถานะของปลายทางสุดท้าย ( สาธิต ):

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

หากคุณไม่ต้องการติดตามการเปลี่ยนเส้นทางคุณสามารถทำได้ในลักษณะเดียวกัน ( สาธิต ):

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

บางส่วนของฟังก์ชั่นตัวเลือกและตัวแปรในการใช้งานมีการอธิบายรายละเอียดเพิ่มเติมเกี่ยวกับการโพสต์บล็อกผมเคยเขียน: HEAD แรกกับ PHP Streams




สำหรับข้อมูลเพิ่มเติมเกี่ยวของ PHP $http_response_headerเห็นphp.net/manual/en/reserved.variables.httpresponseheader.php
Big McLargeHuge

1
ตัวแปรที่สองใช้ได้สำหรับฉันและเมื่อเทียบกับการเรียก file_get_contents เริ่มต้น (ไม่มี stream_context ที่กำหนดเอง) มันเร็วขึ้น 50% เช่นจาก 3,4 ถึง 1,7 สำหรับคำขอ
Erik Čerpnjak

@ ErikČerpnjak: หากไม่มี stream_context แบบ "ไม่กำหนดเอง" จะเป็นค่าเริ่มต้น คุณสามารถรับตัวเลือกจากบริบทเริ่มต้นและดูว่าตัวเลือกเหล่านั้นแตกต่างจากบริบทที่คุณกำหนดเองอย่างไร สิ่งนี้จะให้ข้อมูลเชิงลึกแก่คุณว่าเหตุใดเวลาจึงแตกต่างกัน - php.net/stream-context-get-defaultและphp.net/stream-context-get-options
hakre


6

ฟังก์ชัน inbuilt ของ PHP อาจไม่ทำงานในการตรวจสอบ URL หากการตั้งค่าallow_url_fopenถูกปิดด้วยเหตุผลด้านความปลอดภัย Curl เป็นตัวเลือกที่ดีกว่าเนื่องจากเราไม่จำเป็นต้องเปลี่ยนรหัสของเราในภายหลัง ด้านล่างนี้คือรหัสที่ฉันใช้เพื่อยืนยัน URL ที่ถูกต้อง:

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

โปรดสังเกตตัวเลือกCURLOPT_SSL_VERIFYPEERซึ่งตรวจสอบ URL ที่ขึ้นต้นด้วย HTTPS


6

ในการตรวจสอบการมีอยู่ของภาพexif_imagetypeควรเลือกมากกว่าgetimagesizeเนื่องจากเร็วกว่ามาก

ในการระงับE_NOTICEเพียงแค่นำหน้าตัวดำเนินการควบคุมข้อผิดพลาด ( @)

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

เป็นโบนัสที่มีค่ากลับ ( IMAGETYPE_XXX) จากexif_imagetypeนี้เรายังจะได้รับชนิด mime หรือไฟล์นามสกุลด้วย/image_type_to_mime_typeimage_type_to_extension


4

วิธีแก้ปัญหาที่รุนแรงคือการแสดง Favicons เป็นภาพพื้นหลังใน div เหนือไอคอนเริ่มต้นของคุณ ด้วยวิธีนี้ค่าใช้จ่ายทั้งหมดจะถูกวางไว้บนไคลเอนต์ในขณะที่ยังไม่แสดงภาพที่เสียหาย (ภาพพื้นหลังที่ขาดหายไปจะถูกละเว้นในเบราว์เซอร์ AFAIK ทั้งหมด)


1
+1 หากคุณไม่ได้ตรวจสอบสถานที่หลายแห่งสำหรับไอคอน Fav (favicon.ico, favicon.gif, favicon.png) สิ่งนี้ดูเหมือนจะเป็นทางออกที่ดีที่สุด
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

คุณสามารถใช้สิ่งต่อไปนี้:

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

ทำงานให้ฉันเมื่อพยายามตรวจสอบว่ามีรูปภาพอยู่ใน URL หรือไม่



2

วิธีนี้ใช้ได้ผลสำหรับฉันในการตรวจสอบว่ามีไฟล์ระยะไกลใน PHP หรือไม่:

$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

คุณควรออกคำขอ HEAD ไม่ใช่ GET หนึ่งเพราะคุณไม่ต้องการเนื้อหา URI เลย ดังที่ Pies กล่าวไว้ข้างต้นคุณควรตรวจสอบรหัสสถานะ (ในช่วง 200-299 และคุณอาจเลือกตามการเปลี่ยนเส้นทาง 3xx)

คำถามคำตอบมีตัวอย่างโค้ดจำนวนมากซึ่งอาจเป็นประโยชน์: PHP / Curl: HEAD Request ใช้เวลานานในบางไซต์


1

มีทางเลือกที่ซับซ้อนกว่านี้ คุณสามารถตรวจสอบฝั่งไคลเอ็นต์ทั้งหมดโดยใช้เคล็ดลับ 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); };
});

จากhttp://snipplr.com/view/18782/add-a-favicon-near-external-links-with-jquery/ (ปัจจุบันบล็อกเดิมหยุดให้บริการ)


1

คำตอบทั้งหมดที่นี่ซึ่งใช้ get_headers () กำลังส่งคำขอ GET ทำได้เร็วขึ้น / ถูกกว่ามากในการทำคำขอ HEAD

เพื่อให้แน่ใจว่า get_headers () ส่งคำขอ HEAD แทน GET คุณควรเพิ่มสิ่งนี้:

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

เพื่อตรวจสอบว่ามีไฟล์อยู่หรือไม่รหัสของคุณจะมีลักษณะดังนี้:

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 จะส่งคืนเท็จหรือจริงอย่างชัดเจน


0

ไม่รู้ว่าอันนี้เร็วกว่าไหมเมื่อไฟล์ไม่มีอยู่จากระยะไกลis_file ()แต่คุณสามารถลองถ่ายได้

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

จากเอกสาร: "ใน PHP 5.0.0 ฟังก์ชันนี้ยังสามารถใช้กับเครื่องห่อ URL บางรายการได้โปรดดูโปรโตคอลและ Wrappers ที่สนับสนุนเพื่อพิจารณาว่า Wrapper ใดที่รองรับกลุ่มฟังก์ชัน stat ()"
PatrikAkerstrand

คุณหมายความว่าสิ่งนี้สามารถใช้งานได้หรือไม่หากคุณลงทะเบียน Stream Wrapper แก้ไขคำถามของคุณเพื่อแสดงตัวอย่างการใช้งานและฉันจะลบการไม่ลงคะแนน (และโหวตให้คุณถ้าทำได้) แต่ในขณะนี้ฉันทดสอบ is_file จาก php cli ด้วยไฟล์ระยะไกลและพบว่าเป็นเท็จ
greg0ire

ไม่มีตัวอย่างการทำงาน:var_dump(is_file('http://cdn.sstatic.net/stackoverflow/img/sprites.png')); bool(false)
greg0ire

0

หากไฟล์ไม่ได้โฮสต์ภายนอกคุณอาจแปล URL ระยะไกลเป็นเส้นทางสัมบูรณ์บนเว็บเซิร์ฟเวอร์ของคุณ ด้วยวิธีนี้คุณไม่ต้องเรียก CURL หรือ file_get_contents เป็นต้น

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' );

หมายเหตุ: เว็บเซิร์ฟเวอร์ของคุณต้องเติมข้อมูล DOCUMENT_ROOT เพื่อใช้ฟังก์ชันนี้


0

หากคุณใช้เฟรมเวิร์ก Symfony นอกจากนี้ยังมีวิธีที่ง่ายกว่ามากโดยใช้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;
}

เอกสารสำหรับ HttpClient นั้นดีมากและอาจคุ้มค่าหากคุณต้องการแนวทางที่เฉพาะเจาะจงมากขึ้น: https://symfony.com/doc/current/http_client.html


-1

คุณสามารถใช้ระบบไฟล์: ใช้ Symfony \ Component \ Filesystem \ Filesystem; ใช้ Symfony \ Component \ Filesystem \ Exception \ IOExceptionInterface;

และตรวจสอบ $ fileSystem = new Filesystem (); ถ้า ($ fileSystem-> มีอยู่ ('path_to_file') == จริง) {...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.