Como verificar se existe um arquivo remoto usando PHP?


86

O melhor que pude encontrar, um if fclose fopentipo de coisa, faz a página carregar bem devagar.

Basicamente, o que estou tentando fazer é o seguinte: tenho uma lista de sites e desejo exibir seus favicons ao lado deles. No entanto, se um site não tiver um, gostaria de substituí-lo por outra imagem em vez de exibir uma imagem corrompida.


Acho que você pode usar o CURL e verificar seus códigos de retorno. Mas se é a velocidade que é um problema, basta fazê-lo offline e em cache.
Michał Tatarynowicz

Sim, mas eu ainda recomendaria usar um script offline (executado a partir do cron) que analisa a lista de sites, verifica se eles têm favicons e armazena em cache os dados para o frontend. Se você não pode / não pode usar o cron, pelo menos armazene em cache os resultados para cada nova URL que você verificar.
Michał Tatarynowicz de

3
Para substituir uma imagem quebrada por uma imagem de espaço reservado no navegador, considere uma solução do lado do cliente usando uma onerrorimagem, por exemplo, uma solução usando jQuery

Respostas:


135

Você pode instruir o curl a usar o método HTTP HEAD via CURLOPT_NOBODY.

Mais ou menos

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

De qualquer forma, você economiza apenas no custo da transferência HTTP, não no estabelecimento e fechamento da conexão TCP. E sendo favicons pequenos, você pode não ver muitas melhorias.

Armazenar o resultado localmente em cache parece uma boa ideia se ficar muito lento. HEAD verifica a hora do arquivo e a retorna nos cabeçalhos. Você pode fazer como navegadores e obter o CURLINFO_FILETIME do ícone. Em seu cache, você pode armazenar o URL => [favicon, timestamp]. Você pode então comparar o carimbo de data / hora e recarregar o favicon.


6
apenas uma observação: retcodeerros em todos os 400 códigos para que a validação não fosse >=justa>
Justin Bull

4
Alguns sites bloqueiam o acesso se você não fornecer uma string de agente de usuário, então sugiro seguir este guia para adicionar CURLOPT_USERAGENT além de CURLOPT_NOBODY: davidwalsh.name/set-user-agent-php-curl-spoof
rlorenzo

6
Os códigos retos @Lyth 3XX não são um erro, mas um redirecionamento. Esses devem ser tratados manualmente ou usando CURLOPT_FOLLOWLOCATION.
Ramon Poca,

6
Use curl_setopt ($ ch, CURLOPT_SSL_VERIFYPEER, false); e também para garantir que o mesmo código funcione para URLs começando com HTTPS!
Krishan Gopal

61

Como Pies dizem, você pode usar cURL. Você pode fazer com que o cURL forneça apenas os cabeçalhos, e não o corpo, o que pode torná-lo mais rápido. Um domínio inválido sempre pode demorar um pouco porque você estará aguardando o tempo limite da solicitação expirar; você provavelmente poderia alterar a duração do tempo limite usando cURL.

Aqui está um exemplo:

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/' ) também retornará verdadeiro, mas é apenas um link. Esta função não verifica se o tipo de conteúdo do link é arquivo.
Donatas Navidonskis

36

A solução do CoolGoose é boa, mas é mais rápida para arquivos grandes (pois tenta ler apenas 1 byte):

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

+1. Quais são as desvantagens desta solução em relação à do CURL?
Adriano Varoli Piazza

1
você pode apenas usar fopen- se o código de retorno da solicitação for 404, fopen retornará falso.
s3v3n

isso é muito lento e não funcionou para mim (o que significa que ainda exibia uma imagem quebrada se o caminho do arquivo não estivesse correto)
Helmut

Essa abordagem não funciona se o servidor fizer um redirecionamento sempre que uma imagem ou arquivo não existir. Isso acontece quando um site usa mod_rewrite ou algum tipo de outra "regra" de como as solicitações devem ser tratadas.
Erik Čerpnjak

28

Esta não é uma resposta à sua pergunta original, mas uma maneira melhor de fazer o que você está tentando fazer:

Em vez de tentar obter o favicon do site diretamente (o que é uma pena, pois pode ser /favicon.png, /favicon.ico, /favicon.gif ou mesmo /path/to/favicon.png), use o google:

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

Feito.


4
A sintaxe confunde um pouco. Então, aqui está um exemplo: <img src = " google.com/s2/favicons?domain=stackoverflow.com ">
Habeeb Perwad

19

Uma função completa da resposta mais votada:

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

Você pode usá-lo assim:

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

Oh! Estive fora nos últimos dias, mas o início do mês era quase 24 horas por dia, 7 dias por semana. Obrigado por me avisar!
Pedro Lobito

Isso não funciona se o servidor não responder a nenhum código HTTP (ou o cUrl não o capturar). O que está acontecendo comigo com frequência. Por exemplo. no caso de imagens.
Vaci de

e se o url for redirecionado para outro URL ou versão https? Nesse caso, este código curl não será capaz de fazer o trabalho. a melhor maneira é obter informações de cabeçalho e pesquisar a string "200 ok", que não diferencia maiúsculas de minúsculas.
Infoconic

@Infoconic Você pode adicionar curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);. Eu atualizei a resposta para lidar com 302redirecionamentos.
Pedro Lobito

18

Se você estiver lidando com imagens, use getimagesize. Ao contrário de file_exists, esta função integrada oferece suporte a arquivos remotos. Ele retornará um array que contém as informações da imagem (largura, altura, tipo ... etc). Tudo que você precisa fazer é verificar o primeiro elemento do array (a largura). use print_r para produzir o conteúdo do 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";
}

Resulta em um aviso 404 quando o recurso remoto não está disponível. Por enquanto, lidei com isso suprimindo o erro de uso @na frente de getimagesize, mas me sentindo culpado por esse hack.

No meu caso, essa foi a melhor abordagem, pois sou redirecionado sempre que uma imagem / arquivo não existe. Em segundo lugar, a supressão de erros com @ é proibida, mas neste caso foi necessária.
Erik Čerpnjak

Eu descobri que também poderíamos usar exif_imagetype, e é muito mais rápido stackoverflow.com/a/38295345/1250044
yckart

7

Isso pode ser feito obtendo o código de status HTTP (404 = não encontrado), que é possível com o file_get_contentsDocs usando opções de contexto. O código a seguir leva os redirecionamentos em consideração e retornará o código de status do destino final ( 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 não quiser seguir redirecionamentos, você pode fazer isso de forma semelhante ( 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";

Algumas das funções, opções e variáveis ​​em uso são explicadas com mais detalhes em uma postagem de blog que escrevi: HEAD first with PHP Streams .




Para mais informações sobre PHP, $http_response_headerconsulte php.net/manual/en/reserved.variables.httpresponseheader.php .
Big McLargeHuge

1
A segunda variante funcionou para mim e em comparação com a chamada padrão file_get_contents (sem stream_context customizado) foi 50% mais rápida, ou seja, de 3,4s para 1,7s para uma solicitação.
Erik Čerpnjak

@ ErikČerpnjak: Se não houver "nenhum stream_context personalizado", é o padrão. Você pode obter as opções do contexto padrão e ver como elas variam em relação ao seu contexto personalizado. Isso deve lhe dar algumas dicas sobre por que os tempos são diferentes. - php.net/stream-context-get-default and php.net/stream-context-get-options
hakre

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

Deveria trabalhar ;)


add @ before function
Tebe

6

As funções embutidas do PHP podem não funcionar para verificar a URL se a configuração allow_url_fopen estiver desligada por razões de segurança. Curl é uma opção melhor, pois não precisaríamos alterar nosso código em um estágio posterior. Abaixo está o código que usei para verificar um URL válido:

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

Observe a opção CURLOPT_SSL_VERIFYPEER, que também verifica se o URL começa com HTTPS.


6

Para verificar a existência de imagens, exif_imagetypedeve-se preferir getimagesize, pois é muito mais rápido.

Para suprimir o E_NOTICE, basta acrescentar o operador de controle de erro ( @).

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

Como um bônus, com o valor retornado ( IMAGETYPE_XXX) de exif_imagetypetambém podemos obter o tipo MIME ou a extensão de arquivo com image_type_to_mime_type/ image_type_to_extension.


4

Uma solução radical seria exibir os favicons como imagens de fundo em um div acima do ícone padrão. Dessa forma, toda a sobrecarga seria colocada no cliente, embora ainda não exibisse imagens quebradas (imagens de fundo ausentes são ignoradas em todos os navegadores AFAIK).


1
+1 se você não estiver verificando vários locais para seu favicon (favicon.ico, favicon.gif, favicon.png) esta parece ser a melhor solução
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

Você pode usar o seguinte:

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

Funcionou para mim ao tentar verificar se existe uma imagem no URL


2

Você pode usar :

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

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

2

Isso funciona para mim para verificar se existe um arquivo remoto no 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

Você deve emitir solicitações HEAD, não GET, porque você não precisa do conteúdo URI de forma alguma. Como Pies disse acima, você deve verificar o código de status (em intervalos de 200-299, e você pode opcionalmente seguir redirecionamentos 3xx).

A questão das respostas contém muitos exemplos de código que podem ser úteis: PHP / Curl: Solicitação HEAD demora muito em alguns sites


1

Existe uma alternativa ainda mais sofisticada. Você pode fazer a verificação de todo o lado do cliente usando um truque 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/ (o blog original está atualmente fora do ar)


1

todas as respostas aqui que usam get_headers () estão fazendo uma solicitação GET. É muito mais rápido / mais barato apenas fazer uma solicitação HEAD.

Para garantir que get_headers () faça uma solicitação HEAD em vez de GET, você deve adicionar isto:

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

então, para verificar se existe um arquivo, seu código seria semelhante a este:

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 retornará falso ou verdadeiro, obviamente.


0

Não sei se este é mais rápido quando o arquivo não existe remotamente, is_file () , mas você pode tentar.

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

Dos documentos: "A partir do PHP 5.0.0, esta função também pode ser usada com alguns wrappers de URL. Consulte Protocolos e Wrappers Suportados para determinar quais wrappers suportam a família de funcionalidade stat ()."
PatrikAkerstrand

Você quer dizer que isso poderia funcionar se você registrar um wrapper de fluxo? Edite sua pergunta para mostrar um exemplo de trabalho e eu removerei meu voto negativo (e votarei positivamente em você, se possível). Mas, por enquanto, testei is_file do php cli com um arquivo remoto e obtive falso.
greg0ire

nenhum exemplo de trabalho:var_dump(is_file('http://cdn.sstatic.net/stackoverflow/img/sprites.png')); bool(false)
greg0ire

0

Se o arquivo não estiver hospedado externamente, você pode traduzir o URL remoto em um caminho absoluto em seu servidor da web. Dessa forma, você não precisa chamar 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' );

Nota: Seu servidor da web deve preencher DOCUMENT_ROOT para usar esta função


0

Se você estiver usando o framework Symfony, também há uma maneira muito mais simples de usar 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;
}

Os documentos para o HttpClient também são muito bons e talvez valha a pena dar uma olhada se você precisar de uma abordagem mais específica: https://symfony.com/doc/current/http_client.html


-1

Você pode usar o sistema de arquivos: use Symfony \ Component \ Filesystem \ Filesystem; use Symfony \ Component \ Filesystem \ Exception \ IOExceptionInterface;

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.