Comment vérifier si un fichier distant existe en utilisant PHP?

86

Le meilleur que j'ai pu trouver, un if fclose fopentype de chose, fait que la page se charge très lentement.

En gros, ce que j'essaie de faire est le suivant: j'ai une liste de sites Web et je veux afficher leurs favicons à côté d'eux. Cependant, si un site n'en a pas, j'aimerais le remplacer par une autre image plutôt que d'afficher une image cassée.


la source
Je pense que vous pouvez utiliser CURL et vérifier ses codes de retour. Mais si c'est la vitesse qui pose problème, faites-le simplement hors ligne et mettez en cache.
Michał Tatarynowicz
Oui, mais je recommanderais toujours d'utiliser un script hors ligne (exécuté à partir de cron) qui analyse la liste des sites Web, vérifie s'ils ont des favicons et met en cache ces données pour le frontend. Si vous n'utilisez pas / ne pouvez pas utiliser cron, mettez au moins en cache les résultats pour chaque nouvelle URL que vous vérifiez.
Michał Tatarynowicz
3
Pour remplacer une image cassée par une image d'espace réservé dans le navigateur, veuillez considérer une solution côté client utilisant une onerrorimage, par exemple une solution utilisant jQuery
Possible duplication de PHP: Comment vérifier si un fichier image existe?
Cees Timmerman

Réponses:

135

Vous pouvez demander à curl d'utiliser la méthode HTTP HEAD via CURLOPT_NOBODY.

Plus ou moins

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

Quoi qu'il en soit, vous économisez uniquement le coût du transfert HTTP, pas l'établissement et la fermeture de la connexion TCP. Et comme les favicons sont petites, vous pourriez ne pas voir beaucoup d'amélioration.

La mise en cache du résultat localement semble une bonne idée si elle s'avère trop lente. HEAD vérifie l'heure du fichier et la renvoie dans les en-têtes. Vous pouvez faire comme les navigateurs et obtenir le CURLINFO_FILETIME de l'icône. Dans votre cache, vous pouvez stocker l'URL => [favicon, horodatage]. Vous pouvez ensuite comparer l'horodatage et recharger le favicon.

Ramon Poca
la source
6
juste une note: retcodeerreurs sur les 400 codes donc la validation ne serait >=pas juste>
Justin Bull
4
Certains sites bloquent l'accès si vous ne fournissez pas de chaîne d'agent utilisateur, je suggère donc de suivre ce guide pour ajouter CURLOPT_USERAGENT en plus de CURLOPT_NOBODY: davidwalsh.name/set-user-agent-php-curl-spoof
rlorenzo
6
Les retcodes @Lyth 3XX ne sont pas une erreur, mais une redirection. Ceux-ci doivent être gérés manuellement ou à l'aide de CURLOPT_FOLLOWLOCATION.
Ramon Poca
6
Utilisez curl_setopt ($ ch, CURLOPT_SSL_VERIFYPEER, false); ainsi que pour vous assurer que le même code fonctionne pour les URL commençant par HTTPS!
Krishan Gopal
61

Comme Pies le dit, vous pouvez utiliser cURL. Vous pouvez demander à cURL de ne vous donner que les en-têtes, et non le corps, ce qui pourrait le rendre plus rapide. Un domaine défectueux peut toujours prendre un certain temps car vous attendez que la demande expire; vous pouvez probablement modifier la durée du délai d'expiration en utilisant cURL.

Voici un exemple:

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
la source
3
remoteFileExists (' stackoverflow.com/' ) cela retournera également true, mais ce n'est qu'un lien. Cette fonction ne vérifie pas le type de contenu du lien dans le fichier.
Donatas Navidonskis
36

La solution de CoolGoose est bonne mais c'est plus rapide pour les gros fichiers (car il essaie de lire seulement 1 octet):

if (false === file_get_contents("http://example.com/path/to/image",0,null,0,1)) {
    $image = $default_image;
}
luBar
la source
+1. Y a-t-il quels sont les inconvénients de cette solution par rapport à celle de CURL?
Adriano Varoli Piazza
1
vous pouvez simplement utiliser fopen- si le code de retour de la demande est 404, fopen renvoie false.
s3v3n
c'est vraiment lent et n'a pas fonctionné pour moi (ce qui signifie qu'il affichait toujours une image cassée si le chemin du fichier n'était pas correct)
Helmut
Cette approche ne fonctionne pas si le serveur effectue une redirection chaque fois qu'une image ou un fichier n'existe pas. Cela se produit lorsqu'un site utilise mod_rewrite ou une sorte d'autres "règles" sur la manière dont les requêtes doivent être traitées.
Erik Čerpnjak
28

Ce n'est pas une réponse à votre question initiale, mais une meilleure façon de faire ce que vous essayez de faire:

Au lieu d'essayer d'obtenir directement le favicon du site (ce qui est une douleur royale étant donné qu'il pourrait s'agir de /favicon.png, /favicon.ico, /favicon.gif ou même /path/to/favicon.png), utilisez google:

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

Terminé.

Mala
la source
4
La syntaxe fait un peu confusion. Voici donc un exemple: <img src = " google.com/s2/favicons?domain=stackoverflow.com ">
Habeeb Perwad
19

Une fonction complète de la réponse la plus votée:

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

Vous pouvez l'utiliser comme ceci:

if(remote_file_exists($url))
{
    //file exists, do something
}
Pedro Lobito
la source
Oh! Je suis absent depuis quelques jours mais le début du mois était presque 24h / 24 et 7j / 7. Merci de me l'avoir dit!
Pedro Lobito
Cela ne fonctionne pas si le serveur ne répond à aucun code HTTP (ou si cUrl ne l'attrape pas). Ce qui m'arrive assez souvent. Par exemple. en cas d'images.
Vaci
Que faire si l'URL est redirigée vers une autre URL ou version https? Dans ce cas, ce code curl ne pourra pas faire le travail. le meilleur moyen est d'obtenir les informations d'en-tête et de rechercher la chaîne insensible à la casse "200 ok".
Infoconic
@Infoconic Vous pouvez ajouter curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);. J'ai mis à jour la réponse pour gérer les 302redirections.
Pedro Lobito
18

Si vous utilisez des images, utilisez getimagesize. Contrairement à file_exists, cette fonction intégrée prend en charge les fichiers distants. Il renverra un tableau contenant les informations de l'image (largeur, hauteur, type..etc). Tout ce que vous avez à faire est de vérifier le premier élément du tableau (la largeur). utilisez print_r pour afficher le contenu du tableau

$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
la source
Résultats dans un avertissement 404 lorsque la ressource distante n'est pas disponible. Pour le moment, je l'ai géré en supprimant l'erreur en utilisant @devant getimagesize, mais en me sentant coupable de ce hack.
Dans mon cas, c'était la meilleure approche, car je suis redirigé chaque fois qu'une image / un fichier n'existe pas. Je seconde que la suppression des erreurs avec @ est interdite, mais dans ce cas, c'était nécessaire.
Erik Čerpnjak
J'ai compris que nous pourrions également utiliser exif_imagetype, et c'est beaucoup plus rapide stackoverflow.com/a/38295345/1250044
yckart
7

Cela peut être fait en obtenant le code d'état HTTP (404 = introuvable), ce qui est possible avec file_get_contentsDocs en utilisant les options de contexte. Le code suivant prend en compte les redirections et renverra le code de statut de la destination finale ( Démo ):

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

Si vous ne souhaitez pas suivre les redirections, vous pouvez le faire de la même manière ( Démo ):

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

Certaines des fonctions, options et variables utilisées sont expliquées plus en détail dans un article de blog que j'ai écrit: HEAD first with PHP Streams .

hakre
la source
Pour plus d'informations sur PHP, $http_response_headerconsultez php.net/manual/en/reserved.variables.httpresponseheader.php .
Big McLargeHuge
1
La deuxième variante a fonctionné pour moi et par rapport à l'appel par défaut file_get_contents (pas de stream_context personnalisé), c'était 50% plus rapide, c'est-à-dire de 3,4s à 1,7s pour une requête.
Erik Čerpnjak
@ ErikČerpnjak: S'il n'y a "pas de stream_context personnalisé", c'est celui par défaut. Vous pouvez obtenir les options du contexte par défaut et voir comment elles varient par rapport à votre contexte personnalisé. Cela devrait vous donner un aperçu des raisons pour lesquelles les horaires diffèrent. - php.net/stream-context-get-default et php.net/stream-context-get-options
hakre
6
if (false === file_get_contents("http://example.com/path/to/image")) {
    $image = $default_image;
}

Devrait marcher ;)

CoolGoose
la source
fonction add @ before
Tebe
6

Les fonctions intégrées de PHP peuvent ne pas fonctionner pour vérifier l'URL si le paramètre allow_url_fopen est désactivé pour des raisons de sécurité. Curl est une meilleure option car nous n'aurions pas besoin de changer notre code ultérieurement. Voici le code que j'ai utilisé pour vérifier une URL valide:

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

Veuillez noter l' option CURLOPT_SSL_VERIFYPEER qui vérifie également les URL commençant par HTTPS.

Krishan Gopal
la source
6

Pour vérifier l'existence d'images, exif_imagetypedevrait être préféré getimagesize, car il est beaucoup plus rapide.

Pour supprimer le E_NOTICE, ajoutez simplement l'opérateur de contrôle d'erreur ( @).

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

En prime, avec la valeur retournée ( IMAGETYPE_XXX) de exif_imagetypenous pourrions également obtenir le type mime ou l'extension de fichier avec image_type_to_mime_type/ image_type_to_extension.

yckart
la source
4

Une solution radicale serait d'afficher les favicons comme images d'arrière-plan dans un div au-dessus de votre icône par défaut. De cette façon, tous les frais généraux seraient placés sur le client tout en n'affichant toujours pas les images cassées (les images d'arrière-plan manquantes sont ignorées dans tous les navigateurs AFAIK).

truppo
la source
1
+1 si vous ne vérifiez pas plusieurs emplacements pour leur favicon (favicon.ico, favicon.gif, favicon.png) cela semble être la meilleure solution
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
la source
3

Vous pouvez utiliser les éléments suivants:

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

A travaillé pour moi en essayant de vérifier si une image existe sur l'URL

Rickus Harmse
la source
2

Vous pouvez utiliser :

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

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

Cela fonctionne pour moi pour vérifier si un fichier distant existe en 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';
        }
user7018984
la source
1

Vous devez émettre des requêtes HEAD, pas GET one, car vous n'avez pas du tout besoin du contenu de l'URI. Comme Pies l'a dit ci-dessus, vous devez vérifier le code d'état (dans les plages 200-299, et vous pouvez éventuellement suivre les redirections 3xx).

La question des réponses contient de nombreux exemples de code qui peuvent être utiles: PHP / Curl: HEAD La requête prend beaucoup de temps sur certains sites

drdaeman
la source
1

Il existe une alternative encore plus sophistiquée. Vous pouvez faire la vérification de tout côté client en utilisant une astuce 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); };
});

De http://snipplr.com/view/18782/add-a-favicon-near-external-links-with-jquery/ (le blog original est actuellement en panne)

S Pangborn
la source
1

toutes les réponses ici qui utilisent get_headers () font une requête GET. Il est beaucoup plus rapide / moins cher de faire une requête HEAD.

Pour vous assurer que get_headers () effectue une requête HEAD au lieu d'un GET, vous devez ajouter ceci:

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

donc pour vérifier si un fichier existe, votre code ressemblerait à ceci:

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 retournera soit faux soit vrai, évidemment.

Ludo - Off the record
la source
0

Je ne sais pas si celui-ci est plus rapide lorsque le fichier n'existe pas à distance, is_file () , mais vous pourriez essayer .

$favIcon = 'default FavIcon';
if(is_file($remotePath)) {
   $favIcon = file_get_contents($remotePath);
}
PatrikAkerstrand
la source
Extrait de la documentation: "Depuis PHP 5.0.0, cette fonction peut également être utilisée avec certains wrappers d'URL. Reportez-vous à Protocoles et wrappers pris en charge pour déterminer quels wrappers prennent en charge la famille de fonctionnalités stat ()."
PatrikAkerstrand
Voulez-vous dire que cela pourrait fonctionner si vous enregistrez un wrapper de flux? Modifiez votre question pour afficher un exemple fonctionnel et je supprimerai mon vote défavorable (et je vous voterai si je peux). Mais pour le moment, j'ai testé is_file à partir du cli php avec un fichier distant, et j'ai obtenu false.
greg0ire
aucun exemple de travail:var_dump(is_file('http://cdn.sstatic.net/stackoverflow/img/sprites.png')); bool(false)
greg0ire
0

Si le fichier n'est pas hébergé en externe, vous pouvez traduire l'URL distante en un chemin absolu sur votre serveur Web. De cette façon, vous n'avez pas besoin d'appeler CURL ou file_get_contents, etc.

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

Remarque: votre serveur Web doit remplir DOCUMENT_ROOT pour utiliser cette fonction

Bastian Fießinger
la source
0

Si vous utilisez le framework Symfony, il existe également un moyen beaucoup plus simple d'utiliser le 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;
}

La documentation pour HttpClient est également très bonne et vaut peut-être la peine d'être examinée si vous avez besoin d'une approche plus spécifique: https://symfony.com/doc/current/http_client.html

Filnor
la source
-1

Vous pouvez utiliser le système de fichiers: utilisez Symfony \ Component \ Filesystem \ Filesystem; utilisez Symfony \ Component \ Filesystem \ Exception \ IOExceptionInterface;

et vérifiez $ fileSystem = new Filesystem (); if ($ fileSystem-> existe ('path_to_file') == true) {...

Lenwë Galathil
la source