Wie kann man mit PHP überprüfen, ob eine Remote-Datei vorhanden ist?

85

Das Beste, was ich finden konnte, eine if fclose fopenArt Sache, lässt die Seite sehr langsam laden.

Grundsätzlich versuche ich Folgendes zu tun: Ich habe eine Liste von Websites und möchte deren Favoriten neben ihnen anzeigen. Wenn eine Site jedoch keine hat, möchte ich sie durch ein anderes Bild ersetzen, anstatt ein fehlerhaftes Bild anzuzeigen.


quelle
Ich denke, Sie können CURL verwenden und die Rückkehrcodes überprüfen. Aber wenn es die Geschwindigkeit ist, die ein Problem darstellt, machen Sie es einfach offline und zwischenspeichern.
Michał Tatarynowicz
Ja, aber ich würde trotzdem empfehlen, ein Offline-Skript (von cron ausgeführt) zu verwenden, das die Liste der Websites analysiert, prüft, ob sie Favoriten haben, und diese Daten für das Frontend zwischenspeichert. Wenn Sie cron nicht verwenden können / können, speichern Sie mindestens die Ergebnisse für jede neue URL, die Sie überprüfen.
Michał Tatarynowicz
3
Um ein fehlerhaftes Bild durch ein Platzhalterbild im Browser zu ersetzen, ziehen Sie onerror
Mögliches Duplikat von PHP: Wie überprüfe ich, ob eine Bilddatei vorhanden ist?
Cees Timmerman

Antworten:

134

Sie können curl über CURLOPT_NOBODY anweisen, die HTTP-HEAD-Methode zu verwenden.

Mehr oder weniger

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

Auf jeden Fall sparen Sie nur die Kosten für die HTTP-Übertragung, nicht für den Aufbau und das Schließen der TCP-Verbindung. Und da die Favoriten klein sind, sehen Sie möglicherweise keine große Verbesserung.

Das lokale Zwischenspeichern des Ergebnisses scheint eine gute Idee zu sein, wenn es sich als zu langsam herausstellt. HEAD überprüft die Uhrzeit der Datei und gibt sie in den Headern zurück. Sie können Browser mögen und die CURLINFO_FILETIME des Symbols abrufen. In Ihrem Cache können Sie die URL speichern => [Favicon, Zeitstempel]. Sie können dann den Zeitstempel vergleichen und das Favicon neu laden.

Ramon Poca
quelle
6
Nur eine Anmerkung: retcodeFehler bei allen 400 Codes, damit die Validierung >=nicht nur>
Justin Bull
4
Einige Websites blockieren den Zugriff, wenn Sie keine Benutzeragentenzeichenfolge angeben. Ich empfehle daher, diesem Handbuch zu folgen, um CURLOPT_USERAGENT zusätzlich zu CURLOPT_NOBODY hinzuzufügen: davidwalsh.name/set-user-agent-php-curl-spoof
rlorenzo
6
@ Python 3XX-Retcodes sind kein Fehler, sondern eine Umleitung. Diese sollten entweder manuell oder mit CURLOPT_FOLLOWLOCATION behandelt werden.
Ramon Poca
6
Verwenden Sie curl_setopt ($ ch, CURLOPT_SSL_VERIFYPEER, false); um sicherzustellen, dass derselbe Code auch für URLs funktioniert, die mit HTTPS beginnen!
Krishan Gopal
61

Wie Pies sagen, können Sie cURL verwenden. Sie können cURL veranlassen, nur die Überschriften und nicht den Text anzugeben, wodurch es möglicherweise schneller wird. Eine fehlerhafte Domain kann immer eine Weile dauern, da Sie auf das Zeitlimit für die Anforderung warten. Sie könnten wahrscheinlich die Timeout-Länge mit cURL ändern.

Hier ist ein Beispiel:

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';   
}
Tom Haigh
quelle
3
remoteFileExists (' stackoverflow.com/' ) gibt dies ebenfalls true zurück, aber es ist nur ein Link. Diese Funktion überprüft nicht, ob der Link-Inhaltstyp eine Datei ist.
Donatas Navidonskis
36

Die Lösung von CoolGoose ist gut, bei großen Dateien jedoch schneller (da nur 1 Byte gelesen werden soll):

if (false === file_get_contents("http://example.com/path/to/image",0,null,0,1)) {
    $image = $default_image;
}
luBar
quelle
+1. Gibt es die Nachteile dieser Lösung gegenüber der CURL-Lösung?
Adriano Varoli Piazza
1
Sie können einfach verwenden fopen- wenn der Anforderungsrückgabecode 404 lautet, gibt fopen false zurück.
s3v3n
Dies ist sehr langsam und hat bei mir nicht funktioniert (was bedeutet, dass immer noch ein fehlerhaftes Bild angezeigt wird, wenn der Dateipfad nicht korrekt ist)
Helmut
Dieser Ansatz funktioniert nicht, wenn der Server eine Umleitung vornimmt, wenn ein Bild oder eine Datei nicht vorhanden ist. Dies geschieht, wenn eine Site mod_rewrite oder eine andere Art von "Regeln" verwendet, wie Anforderungen behandelt werden sollen.
Erik Čerpnjak
28

Dies ist keine Antwort auf Ihre ursprüngliche Frage, sondern eine bessere Möglichkeit, das zu tun, was Sie versuchen:

Anstatt tatsächlich zu versuchen, das Favicon der Site direkt abzurufen (was ein königlicher Schmerz ist, da es /favicon.png, /favicon.ico, /favicon.gif oder sogar /path/to/favicon.png sein könnte), verwenden Sie Google:

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

Getan.

Mala
quelle
4
Die Syntax macht ein bisschen Verwirrung. Also hier ein Beispiel: <img src = " google.com/s2/favicons?domain=stackoverflow.com ">
Habeeb Perwad
18

Wenn Sie mit Bildern arbeiten, verwenden Sie getimagesize. Im Gegensatz zu file_exists unterstützt diese integrierte Funktion Remote-Dateien. Es wird ein Array zurückgegeben, das die Bildinformationen enthält (Breite, Höhe, Typ usw.). Sie müssen lediglich das erste Element im Array (die Breite) überprüfen. Verwenden Sie print_r, um den Inhalt des Arrays auszugeben

$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";
}
Eyad Fallatah
quelle
Führt zu einer Warnung 404, wenn die Remote-Ressource nicht verfügbar ist. Vorläufig habe ich damit umgegangen, indem ich Fehler unterdrückt habe, indem ich mich @vor getimagesize, aber für diesen Hack schuldig gefühlt habe.
In meinem Fall war dies der beste Ansatz, da ich immer dann umgeleitet werde, wenn ein Bild / eine Datei nicht vorhanden ist. Ich zweitens, dass das Unterdrücken von Fehlern mit @ ein No-Go ist, aber in diesem Fall war es notwendig.
Erik Čerpnjak
Ich fand heraus, dass wir auch verwenden könnten exif_imagetype, und es ist viel schneller stackoverflow.com/a/38295345/1250044
yckart
17

Eine vollständige Funktion der am häufigsten gewählten Antwort:

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

Sie können es so verwenden:

if(remote_file_exists($url))
{
    //file exists, do something
}
CONvid19
quelle
Oh! Ich war die letzten Tage weg, aber der Anfang des Monats war fast 24/7. Vielen Dank, dass Sie mich informiert haben!
CONvid19
Dies funktioniert nicht, wenn der Server keinen HTTP-Code antwortet (oder cUrl ihn nicht abfängt). Was mir ziemlich oft passiert. Z.B. im Falle von Bildern.
Vaci
Was passiert, wenn die URL zu einer anderen URL oder https-Version umgeleitet wird? In diesem Fall kann dieser Curl-Code die Aufgabe nicht ausführen. Der beste Weg ist, Header-Informationen abzurufen und nach der Zeichenfolge "200 ok" zu suchen, bei der die Groß- und Kleinschreibung nicht berücksichtigt wird.
Infoconic
@Infoconic Sie können hinzufügen curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);. Ich habe die Antwort aktualisiert, um 302Weiterleitungen zu verarbeiten .
CONvid19
7

Dies kann erreicht werden, indem der HTTP-Statuscode (404 = nicht gefunden) abgerufen wird, der mit file_get_contentsDocs möglich ist, die Kontextoptionen verwenden. Der folgende Code berücksichtigt Weiterleitungen und gibt den Statuscode des endgültigen Ziels ( Demo ) zurück:

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

Wenn Sie Weiterleitungen nicht folgen möchten, können Sie dies auf ähnliche Weise tun ( 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";

Einige der verwendeten Funktionen, Optionen und Variablen werden in einem Blog-Beitrag, den ich geschrieben habe, ausführlicher erläutert: HEAD first with PHP Streams .

hakre
quelle
Weitere Informationen zu PHPs finden$http_response_header Sie unter php.net/manual/en/reserved.variables.httpresponseheader.php .
Big McLargeHuge
1
Die zweite Variante funktionierte für mich und war im Vergleich zum Standardaufruf file_get_contents (kein benutzerdefinierter stream_context) 50% schneller, dh von 3,4 bis 1,7 Sekunden für eine Anfrage.
Erik Čerpnjak
@ ErikČerpnjak: Wenn es "keinen benutzerdefinierten" stream_context gibt, ist es der Standard. Sie können die Optionen aus dem Standardkontext abrufen und sehen, wie sie sich von Ihrem benutzerdefinierten Kontext unterscheiden. Dies sollte Ihnen einen Einblick geben, warum sich die Zeiten unterscheiden. - php.net/stream-context-get-default und php.net/stream-context-get-options
hakre
6
if (false === file_get_contents("http://example.com/path/to/image")) {
    $image = $default_image;
}

Sollte arbeiten ;)

CoolGoose
quelle
add @ before function
Tebe
6

Die in PHP integrierten Funktionen funktionieren möglicherweise nicht zum Überprüfen der URL, wenn die Einstellung allow_url_fopen aus Sicherheitsgründen deaktiviert ist. Curl ist eine bessere Option, da wir unseren Code zu einem späteren Zeitpunkt nicht mehr ändern müssten. Unten ist der Code, mit dem ich eine gültige URL überprüft habe:

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

Bitte beachten Sie die Option CURLOPT_SSL_VERIFYPEER, mit der auch die URLs überprüft werden, die mit HTTPS beginnen.

Krishan Gopal
quelle
6

Um zu überprüfen, ob Bilder vorhanden sind, exif_imagetypesollte dies vorgezogen werden getimagesize, da es viel schneller ist.

Um dies zu unterdrücken E_NOTICE, stellen Sie einfach den Fehlerkontrolloperator ( @) voran .

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

Als Bonus könnten wir mit dem zurückgegebenen Wert ( IMAGETYPE_XXX) von exif_imagetypeauch den MIME-Typ oder die Dateierweiterung mit image_type_to_mime_type/ erhalten image_type_to_extension.

yckart
quelle
4

Eine radikale Lösung wäre, die Favoriten als Hintergrundbilder in einem Div über Ihrem Standardsymbol anzuzeigen. Auf diese Weise wird der gesamte Overhead auf dem Client platziert, ohne dass fehlerhafte Bilder angezeigt werden (fehlende Hintergrundbilder werden in allen AFAIK-Browsern ignoriert).

Truppo
quelle
1
+1 Wenn Sie nicht mehrere Standorte auf ihr Favicon überprüfen (favicon.ico, favicon.gif, favicon.png), scheint dies die beste Lösung zu sein
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!!!";
    }
dr.linux
quelle
3

Sie könnten Folgendes verwenden:

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

Hat bei mir funktioniert, als ich versucht habe zu überprüfen, ob ein Bild in der URL vorhanden ist

Rickus Harmse
quelle
2

Sie können verwenden:

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

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

Dies funktioniert bei mir, um zu überprüfen, ob eine Remote-Datei in PHP vorhanden ist:

$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';
        }
user7018984
quelle
1

Sie sollten HEAD-Anforderungen ausgeben, nicht GET one, da Sie den URI-Inhalt überhaupt nicht benötigen. Wie Pies oben sagte, sollten Sie nach dem Statuscode suchen (in Bereichen von 200 bis 299, und Sie können optional 3xx-Weiterleitungen folgen).

Die Antwortfrage enthält viele Codebeispiele, die hilfreich sein können: PHP / Curl: HEAD Request dauert auf einigen Websites sehr lange

Drdaeman
quelle
1

Es gibt eine noch raffiniertere Alternative. Sie können die gesamte clientseitige Überprüfung mit einem JQuery-Trick durchführen.

$('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); };
});

Von http://snipplr.com/view/18782/add-a-favicon-near-external-links-with-jquery/ (der ursprüngliche Blog ist derzeit nicht verfügbar)

S Pangborn
quelle
1

Alle Antworten hier, die get_headers () verwenden, führen eine GET-Anfrage aus. Es ist viel schneller / billiger, nur eine HEAD-Anfrage zu stellen.

Um sicherzustellen, dass get_headers () eine HEAD-Anforderung anstelle eines GET ausführt, sollten Sie Folgendes hinzufügen:

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

Um zu überprüfen, ob eine Datei vorhanden ist, sieht Ihr Code ungefähr so ​​aus:

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 gibt offensichtlich entweder false oder true zurück.

Ludo - Inoffiziell
quelle
0

Ich weiß nicht, ob dies schneller ist, wenn die Datei nicht remote existiert, is_file () , aber Sie könnten es versuchen .

$favIcon = 'default FavIcon';
if(is_file($remotePath)) {
   $favIcon = file_get_contents($remotePath);
}
PatrikAkerstrand
quelle
Aus den Dokumenten: "Ab PHP 5.0.0 kann diese Funktion auch mit einigen URL-Wrappern verwendet werden. Informationen zu den Wrappern, die die stat () - Funktionsfamilie unterstützen, finden Sie unter Unterstützte Protokolle und Wrapper."
PatrikAkerstrand
Meinen Sie damit, dass dies funktionieren könnte, wenn Sie einen Stream-Wrapper registrieren? Bearbeiten Sie Ihre Frage, um ein funktionierendes Beispiel zu zeigen, und ich werde meine Abwertung entfernen (und Sie aufstimmen, wenn ich kann). Aber im Moment habe ich is_file von der PHP-CLI mit einer Remote-Datei getestet und bin falsch geworden.
Greg0ire
kein funktionierendes Beispiel:var_dump(is_file('http://cdn.sstatic.net/stackoverflow/img/sprites.png')); bool(false)
Greg0ire
0

Wenn die Datei nicht extern gehostet wird, können Sie die Remote-URL in einen absoluten Pfad auf Ihrem Webserver übersetzen. Auf diese Weise müssen Sie weder CURL noch file_get_contents usw. aufrufen.

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

Hinweis: Ihr Webserver muss DOCUMENT_ROOT ausfüllen, um diese Funktion verwenden zu können

Bastian Fießinger
quelle
0

Wenn Sie das Symfony-Framework verwenden, gibt es auch eine viel einfachere Möglichkeit 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;
}

Die Dokumente für den HttpClient sind ebenfalls sehr gut und möglicherweise einen Blick wert, wenn Sie einen genaueren Ansatz benötigen: https://symfony.com/doc/current/http_client.html

Filnor
quelle
-1

Sie können das Dateisystem verwenden: Verwenden Sie Symfony \ Component \ Filesystem \ Filesystem; Verwenden Sie Symfony \ Component \ Filesystem \ Exception \ IOExceptionInterface.

und überprüfe $ fileSystem = new Filesystem (); if ($ fileSystem-> existiert ('path_to_file') == true) {...

Lenwë Galathil
quelle