Come si può verificare se esiste un file remoto utilizzando PHP?


86

Il meglio che ho trovato, una if fclose fopencosa tipo, fa caricare la pagina molto lentamente.

Fondamentalmente quello che sto cercando di fare è il seguente: ho un elenco di siti Web e desidero visualizzare le loro favicon accanto a loro. Tuttavia, se un sito non ne ha uno, vorrei sostituirlo con un'altra immagine piuttosto che visualizzare un'immagine interrotta.


Penso che tu possa usare CURL e controllare i suoi codici di ritorno. Ma se è la velocità a essere un problema, fallo offline e cache.
Michał Tatarynowicz,

Sì, ma consiglierei comunque di utilizzare uno script offline (eseguito da cron) che analizza l'elenco dei siti Web, controlla se hanno favicon e memorizza nella cache i dati per il frontend. Se non usi / non puoi usare cron, memorizza almeno nella cache i risultati per ogni nuovo URL che controlli.
Michał Tatarynowicz

3
Per sostituire un'immagine danneggiata con un'immagine segnaposto nel browser, prendi in considerazione una soluzione lato client utilizzando l' onerrorimmagine, ad esempio una soluzione che utilizza jQuery

Risposte:


135

Puoi indicare a curl di utilizzare il metodo HTTP HEAD tramite CURLOPT_NOBODY.

Più o meno

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

Ad ogni modo, risparmi solo il costo del trasferimento HTTP, non la creazione e la chiusura della connessione TCP. Ed essendo le favicon piccole, potresti non vedere molti miglioramenti.

Memorizzare il risultato nella cache localmente sembra una buona idea se risulta essere troppo lento. HEAD controlla l'ora del file e lo restituisce nelle intestazioni. Puoi fare come i browser e ottenere il CURLINFO_FILETIME dell'icona. Nella cache puoi memorizzare l'URL => [favicon, timestamp]. È quindi possibile confrontare il timestamp e ricaricare la favicon.


6
solo una nota: retcodeerrori su tutti i 400 codici quindi la convalida non sarebbe >=solo>
Justin Bull

4
Alcuni siti bloccano l'accesso se non fornisci una stringa dello user agent, quindi suggerisco di seguire questa guida per aggiungere CURLOPT_USERAGENT oltre a CURLOPT_NOBODY: davidwalsh.name/set-user-agent-php-curl-spoof
rlorenzo

6
@Lyth 3XX ricodifica non è un errore, ma un reindirizzamento. Questi dovrebbero essere gestiti manualmente o utilizzando CURLOPT_FOLLOWLOCATION.
Ramon Poca

6
Usa curl_setopt ($ ch, CURLOPT_SSL_VERIFYPEER, false); anche per assicurarti che lo stesso codice funzioni per gli URL che iniziano con HTTPS!
Krishan Gopal

61

Come dice Pies puoi usare cURL. Puoi fare in modo che cURL ti dia solo le intestazioni e non il corpo, il che potrebbe renderlo più veloce. Un dominio errato potrebbe sempre richiedere del tempo perché dovrai attendere il timeout della richiesta; probabilmente potresti cambiare la durata del timeout usando cURL.

Ecco un esempio:

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/ ' ) restituirà anche true, ma è solo un collegamento. Questa funzione non verifica se il tipo di contenuto del collegamento è file.
Donatas Navidonskis

36

La soluzione di CoolGoose è buona ma è più veloce per file di grandi dimensioni (poiché cerca di leggere solo 1 byte):

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

+1. Quali sono gli svantaggi di questa soluzione rispetto a quella CURL?
Adriano Varoli Piazza

1
puoi semplicemente usare fopen- se il codice di ritorno della richiesta è 404, fopen restituisce false.
s3v3n

questo è molto lento e non ha funzionato per me (il che significa che mostrava ancora un'immagine rotta se il percorso del file non era corretto)
Helmut

Questo approccio non funziona se il server esegue un reindirizzamento ogni volta che un'immagine o un file non esiste. Questo accade quando un sito utilizza mod_rewrite o una sorta di altre "regole" su come gestire le richieste.
Erik Čerpnjak

28

Questa non è una risposta alla tua domanda originale, ma un modo migliore di fare ciò che stai cercando di fare:

Invece di provare a ottenere direttamente la favicon del sito (il che è un problema reale dato che potrebbe essere /favicon.png, /favicon.ico, /favicon.gif o anche /path/to/favicon.png), usa google:

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

Fatto.


4
La sintassi crea un po 'di confusione. Quindi ecco un esempio: <img src = " google.com/s2/favicons?domain=stackoverflow.com ">
Habeeb Perwad

19

Una funzione completa della risposta più votata:

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

Puoi usarlo in questo modo:

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

Oh! Sono stato via negli ultimi due giorni, ma l'inizio del mese è stato quasi 24 ore su 24, 7 giorni su 7. Grazie per avermi fatto sapere!
Pedro Lobito

Questo non funziona se il server non risponde ad alcun codice HTTP (o cUrl non lo rileva). Il che mi sta accadendo abbastanza spesso. Per esempio. in caso di immagini.
Vaci

cosa succede se l'URL viene reindirizzato a un altro URL o una versione https? In tal caso, questo codice curl non sarà in grado di svolgere il lavoro. il modo migliore è ottenere le informazioni di intestazione e cercare la stringa senza distinzione tra maiuscole e minuscole "200 ok".
Infoconic

@ Infoconic È possibile aggiungere curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);. Ho aggiornato la risposta per gestire i 302reindirizzamenti.
Pedro Lobito

18

Se hai a che fare con immagini, usa getimagesize. A differenza di file_exists, questa funzione integrata supporta i file remoti. Restituirà un array che contiene le informazioni sull'immagine (larghezza, altezza, tipo ... ecc.). Tutto quello che devi fare è controllare il primo elemento dell'array (la larghezza). usa print_r per visualizzare il contenuto dell'array

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

Produce un avviso 404 quando la risorsa remota non è disponibile. Per il momento, l'ho gestito sopprimendo l'errore usando @davanti getimagesize, ma sentendomi in colpa per questo hack.

Nel mio caso questo è stato l'approccio migliore, perché vengo reindirizzato ogni volta che un'immagine / file non esiste. Secondo che la soppressione degli errori con @ non è possibile, ma in questo caso è stata necessaria.
Erik Čerpnjak

Ho capito che potremmo anche usare exif_imagetype, ed è molto più veloce stackoverflow.com/a/38295345/1250044
yckart

7

Questo può essere fatto ottenendo il codice di stato HTTP (404 = non trovato), possibile con file_get_contentsDocumenti che utilizzano le opzioni di contesto. Il codice seguente tiene conto dei reindirizzamenti e restituirà il codice di stato della destinazione finale ( 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";

Se non vuoi seguire i reindirizzamenti, puoi farlo in modo simile ( 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";

Alcune delle funzioni, opzioni e variabili in uso sono spiegate con maggiori dettagli in un post del blog che ho scritto: HEAD prima con PHP Streams .




Per ulteriori informazioni su PHP, $http_response_headervedere php.net/manual/en/reserved.variables.httpresponseheader.php .
Big McLargeHuge

1
La seconda variante ha funzionato per me e rispetto alla chiamata predefinita file_get_contents (no custom stream_context) era il 50% più veloce, cioè da 3,4s a 1,7s per una richiesta.
Erik Čerpnjak

@ ErikČerpnjak: Se non è presente stream_context "personalizzato", è quello predefinito. Puoi ottenere le opzioni dal contesto predefinito e dare un'occhiata a come variano dal tuo contesto personalizzato. Questo dovrebbe darti un'idea del perché i tempi differiscono. - php.net/stream-context-get-default e php.net/stream-context-get-options
hakre

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

Dovrebbe funzionare ;)


aggiungi @ prima della funzione
Tebe

6

Le funzioni integrate di PHP potrebbero non funzionare per il controllo dell'URL se l' impostazione allow_url_fopen è disattivata per motivi di sicurezza. Curl è un'opzione migliore in quanto non avremmo bisogno di modificare il nostro codice in una fase successiva. Di seguito è riportato il codice che ho utilizzato per verificare un URL valido:

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

Si prega di notare l' opzione CURLOPT_SSL_VERIFYPEER che verifica anche che l'URL inizi con HTTPS.


6

Per verificare l'esistenza di immagini, exif_imagetypedovrebbe essere preferito rispetto getimagesize, in quanto è molto più veloce.

Per sopprimere E_NOTICE, è sufficiente anteporre l'operatore di controllo degli errori ( @).

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

Come bonus, con il valore restituito ( IMAGETYPE_XXX) da exif_imagetypepotremmo anche ottenere il tipo MIME o l'estensione file con image_type_to_mime_type/ image_type_to_extension.


4

Una soluzione radicale sarebbe quella di visualizzare le favicon come immagini di sfondo in un div sopra l'icona predefinita. In questo modo, tutto l'overhead verrebbe posizionato sul client senza visualizzare ancora le immagini interrotte (le immagini di sfondo mancanti vengono ignorate in tutti i browser AFAIK).


1
+1 se non stai controllando più posizioni per la loro favicon (favicon.ico, favicon.gif, favicon.png) questa sembra essere la soluzione migliore
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

Potresti usare quanto segue:

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

Ha funzionato per me quando ho provato a verificare se un'immagine esiste sull'URL


2

Puoi usare :

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

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

2

Questo funziona per me per verificare se esiste un file remoto in 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

Dovresti emettere richieste HEAD, non GET una, perché non hai affatto bisogno del contenuto dell'URI. Come ha detto Pies sopra, dovresti controllare il codice di stato (in intervalli 200-299 e puoi facoltativamente seguire i reindirizzamenti 3xx).

La domanda di risposta contiene molti esempi di codice che possono essere utili: PHP / Curl: la richiesta HEAD richiede molto tempo su alcuni siti


1

C'è un'alternativa ancora più sofisticata. Puoi fare il controllo di tutto il lato client usando un trucco 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); };
});

Da http://snipplr.com/view/18782/add-a-favicon-near-external-links-with-jquery/ (il blog originale è attualmente inattivo)


1

tutte le risposte qui che usano get_headers () stanno facendo una richiesta GET. È molto più veloce / più economico fare solo una richiesta HEAD.

Per assicurarti che get_headers () esegua una richiesta HEAD invece di una GET dovresti aggiungere questo:

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

quindi per verificare se esiste un file, il tuo codice sarebbe simile a questo:

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 restituirà ovviamente false o true.


0

Non so se questo è più veloce quando il file non esiste in remoto, is_file () , ma potresti provarlo .

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

Dalla documentazione: "A partire da PHP 5.0.0, questa funzione può essere utilizzata anche con alcuni wrapper URL. Fare riferimento a Protocolli e wrapper supportati per determinare quali wrapper supportano la famiglia di funzionalità stat ()."
PatrikAkerstrand

Vuoi dire che questo potrebbe funzionare se registri un wrapper di flusso? Modifica la tua domanda per mostrare un esempio funzionante e rimuoverò il mio voto negativo (e ti voterò se posso). Ma per il momento, ho testato is_file dal php cli con un file remoto e ho ottenuto false.
greg0ire

nessun esempio funzionante:var_dump(is_file('http://cdn.sstatic.net/stackoverflow/img/sprites.png')); bool(false)
greg0ire

0

Se il file non è ospitato all'esterno, potresti tradurre l'URL remoto in un percorso assoluto sul tuo server web. In questo modo non devi chiamare CURL o file_get_contents, ecc.

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

Nota: il tuo server web deve compilare DOCUMENT_ROOT per utilizzare questa funzione


0

Se stai usando il framework Symfony, c'è anche un modo molto più semplice di usare 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;
}

Anche i documenti per HttpClient sono molto buoni e forse vale la pena esaminarli se è necessario un approccio più specifico: https://symfony.com/doc/current/http_client.html


-1

Puoi usare il filesystem: usa Symfony \ Component \ Filesystem \ Filesystem; usa Symfony \ Component \ Filesystem \ Exception \ IOExceptionInterface;

e controlla $ fileSystem = new Filesystem (); if ($ fileSystem-> exist ('path_to_file') == true) {...

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.