Baixar arquivo para servidor a partir do URL

341

Bem, este parece bastante simples, e é. Tudo o que você precisa fazer para baixar um arquivo no seu servidor é:

file_put_contents("Tmpfile.zip", file_get_contents("http://someurl/file.zip"));

Só há um problema. E se você tiver um arquivo grande, como 100mb. Você ficará sem memória e não poderá baixar o arquivo.

O que eu quero é uma maneira de gravar o arquivo no disco durante o download. Dessa forma, eu posso baixar arquivos maiores, sem ter problemas de memória.

xaav
fonte
4
Isso está definido na configuração do servidor, o PHP não pode contorná-lo tanto quanto eu sei (exceto por uma edição direta de .ini) #
215 Ben Ben

Respostas:

494

Desde o PHP 5.1.0, file_put_contents()suporta escrever peça por peça passando um identificador de fluxo como o $dataparâmetro:

file_put_contents("Tmpfile.zip", fopen("http://someurl/file.zip", 'r'));

Do manual:

Se os dados [esse é o segundo argumento] forem um recurso de fluxo, o buffer restante desse fluxo será copiado para o arquivo especificado. Isso é semelhante ao uso stream_copy_to_stream().

(Obrigado Hakre .)

alex
fonte
4
Essa não seria minha primeira escolha. Se allow_fopen_url Offestiver definido no php.ini (boa idéia para segurança), seu script será quebrado.
PleaseStand
4
@idealmachine Acho que file_get_contents()também não funcionaria se fosse esse o caso (consulte OP).
alex
10
@geoff Eu era específico, mencionei a função que você queria. O que você pode querer é que alguém escreva o código para você - mas tenho certeza que você aprendeu algo fazendo isso sozinho. Além disso, se nós estamos indo para comentário sobre os outros é tão interações - por favor, aceite mais algumas respostas :)
alex
@alex: Por favor, veja a edição, fique à vontade para incorporar. avise-me quando eu puder remover este comentário aqui.
hakre 31/03
4
O sinalizador 'b' também deve ser usado na maioria dos casos com fopen; evita efeitos adversos a imagens e outros arquivos de texto não simples.
Wayne Weibel 12/09
132
private function downloadFile($url, $path)
{
    $newfname = $path;
    $file = fopen ($url, 'rb');
    if ($file) {
        $newf = fopen ($newfname, 'wb');
        if ($newf) {
            while(!feof($file)) {
                fwrite($newf, fread($file, 1024 * 8), 1024 * 8);
            }
        }
    }
    if ($file) {
        fclose($file);
    }
    if ($newf) {
        fclose($newf);
    }
}
xaav
fonte
11
obrigado pelo seu snippit, mas você poderia explicar seu código @xaav? Eu não sou exatamente brilhante em php. Para que serve 1024 * 8? Obrigado novamente.
vvMINOvv
@wMINOw O comprimento da linha.
David Bélanger
2
Especificamente, significa ler até 8 KB por vez (1024 bytes por KB * 8), pois o parâmetro está em bytes. Enquanto a linha for <= 8KB, ela lerá a linha inteira de uma só vez.
Doktor J
11
Por que essa não é a melhor resposta?
GunJack
11
Como você lida com erros com essa abordagem? E se um 404 for retornado ou a conexão for interrompida ou atingir o tempo limite?
Adam Swinden
67

Tente usar cURL

set_time_limit(0); // unlimited max execution time
$options = array(
  CURLOPT_FILE    => '/path/to/download/the/file/to.zip',
  CURLOPT_TIMEOUT =>  28800, // set this to 8 hours so we dont timeout on big files
  CURLOPT_URL     => 'http://remoteserver.com/path/to/big/file.zip',
);

$ch = curl_init();
curl_setopt_array($ch, $options);
curl_exec($ch);
curl_close($ch);

Não tenho certeza, mas acredito que com a CURLOPT_FILEopção que ele escreve, ele puxa os dados, ou seja. sem buffer.

prodigitalson
fonte
2
Normalmente, isso seria bom, mas eu tenho esse código em um aplicativo Web, por isso não posso garantir que os usuários tenham o cURL instalado. No entanto, eu votei nisso.
xaav
@ Geoff é um aplicativo da web distribuído? Porque se você controla a hospedagem, isso não importa para os usuários (cURL é uma biblioteca no seu servidor).
alex
Não. Eu não controlo a hospedagem. É um aplicativo da web distribuído que qualquer um poderia ter.
xaav
3
A curvatura pode estar ausente. Mas quase todas as empresas de hospedagem compartilhada têm o CURL instalado por padrão. Quero dizer, eu não vi um que não.
Mangirdas Skripka
19
A partir dos meus testes, você não pode atribuir a CURLOPT_FILE um caminho de arquivo diretamente. Tem que ser um manipulador de arquivos. Primeiro, abra o arquivo com $fh = fopen('/path/to/download/the/file/to.zip', 'w');e feche com fclose($fh);depois curl_close($ch);. E prontoCURLOPT_FILE => $fh
Gustavo
22

a resposta do prodigitalson não funcionou para mim. Eu tenho missing fopen in CURLOPT_FILE mais detalhes .

Isso funcionou para mim, incluindo URLs locais:

function downloadUrlToFile($url, $outFileName)
{   
    if(is_file($url)) {
        copy($url, $outFileName); 
    } else {
        $options = array(
          CURLOPT_FILE    => fopen($outFileName, 'w'),
          CURLOPT_TIMEOUT =>  28800, // set this to 8 hours so we dont timeout on big files
          CURLOPT_URL     => $url
        );

        $ch = curl_init();
        curl_setopt_array($ch, $options);
        curl_exec($ch);
        curl_close($ch);
    }
}
Kamil Kiełczewski
fonte
19
  1. Crie uma pasta chamada "downloads" no servidor de destino
  2. Salve [este código] no .phparquivo e execute no servidor de destino

Downloader:

<html>
<form method="post">
<input name="url" size="50" />
<input name="submit" type="submit" />
</form>
<?php
    // maximum execution time in seconds
    set_time_limit (24 * 60 * 60);

    if (!isset($_POST['submit'])) die();

    // folder to save downloaded files to. must end with slash
    $destination_folder = 'downloads/';

    $url = $_POST['url'];
    $newfname = $destination_folder . basename($url);

    $file = fopen ($url, "rb");
    if ($file) {
      $newf = fopen ($newfname, "wb");

      if ($newf)
      while(!feof($file)) {
        fwrite($newf, fread($file, 1024 * 8 ), 1024 * 8 );
      }
    }

    if ($file) {
      fclose($file);
    }

    if ($newf) {
      fclose($newf);
    }
?>
</html> 
stra8edge
fonte
Isso pressupõe que o usuário deseja um script independente em vez de uma solução que funcione em um aplicativo PHP existente, e acredito que o último seja o que o OP e a maioria dos outros estão procurando. Uma explicação também seria útil para pessoas que desejam entender a abordagem.
Sean the Bean
11
sempre que tento isso, meu tamanho de arquivo transferido é 50816, mas meu tamanho é maior que isso .. 120MB .. Alguma idéia do porquê disso?
Riffaz Starr
set_time_limit (24 * 60 * 60);tem que ser colocado dentro de um loop. Não tem efeito no início do script.
Viktor Joras
16
set_time_limit(0); 
$file = file_get_contents('path of your file');
file_put_contents('file.ext', $file);
Dimmy
fonte
sua resposta é muito simples e funciona bem, me ajudou onde o cURL não conseguiu obter o arquivo, isso funcionou. Obrigado :)
Tommix
2
Você pode explicar o que isso realmente faz.
alex
6
Isso não resolve o problema do OP de exceder o limite de memória do PHP.
user9645
Isso é bem simples e direto. Bastante útil para casos mais simples em que os arquivos são pequenos ou o ambiente é um desenvolvimento local.
Valentine Shi
alguma idéia para arquivos .xlsx? Está armazenando um arquivo vazio com 0 byte de memória.
Dhruv Thakkar
9

Existem 3 maneiras:

  1. file_get_contents e file_put_contents
  2. ONDULAÇÃO
  3. abrir

Você pode encontrar exemplos aqui .

Hoan Huynh
fonte
8

Use um método simples em php copy()

copy($source_url, $local_path_with_file_name);

Nota: se o arquivo de destino já existir, ele será substituído

Função PHP copy ()

Nota: Você precisa definir a permissão 777 para a pasta de destino. Use este método ao fazer o download para a sua máquina local.

Nota especial: 777 é uma permissão no sistema baseado em Unix com permissão total de leitura / gravação / execução para proprietário, grupo e todos. Em geral, concedemos essa permissão a ativos que não são muito necessários para serem ocultados do público em um servidor da web. Exemplo: pasta de imagens.

Pradeep Kumar Prabaharan
fonte
11
Eu nunca, nunca, nunca definirei o 777 como permissões em um servidor da Web e inicializarei qualquer desenvolvedor da Web que tenha a má idéia de fazer isso. Toda vez, em todo lugar. Cuidado ! Você não pode fazer isso ! Pense em segurança. Seguir as regras da OWASP não é suficiente. Ter um bom pensamento sobre coisas simples é importante.
ThiagoJC12
@ThierryB. Nota: Eu dei o caminho local. e isso pode ser usado em aplicativos internos. Ter uma boa leitura e compreensão das perguntas e respostas. Pense em diferentes cenários. E isso não é aceito / melhor resposta. Cada pergunta tem respostas diferentes, com prós e contras. Exemplo para você entender: Até Fibonacci tem várias soluções exclusivas, onde apenas uma será a melhor. Outros serão usados ​​em diferentes cenários.
Pradeep Kumar Prabaharan
Ok, mas dedicar um tempo para pensar nas melhores práticas e implementá-las em locais seguros dará uma melhor compreensão dos conceitos que você deve implementar. Talvez se um intruso está dentro de seu ($) em casa, fazendo algumas armadilhas ou construir coisas da melhor maneira que pode lhe dará algumas dores de cabeça;)
thierryb
5

Eu uso isso para baixar o arquivo

function cURLcheckBasicFunctions()
{
  if( !function_exists("curl_init") &&
      !function_exists("curl_setopt") &&
      !function_exists("curl_exec") &&
      !function_exists("curl_close") ) return false;
  else return true;
}

/*
 * Returns string status information.
 * Can be changed to int or bool return types.
 */
function cURLdownload($url, $file)
{
  if( !cURLcheckBasicFunctions() ) return "UNAVAILABLE: cURL Basic Functions";
  $ch = curl_init();
  if($ch)
  {

    $fp = fopen($file, "w");
    if($fp)
    {
      if( !curl_setopt($ch, CURLOPT_URL, $url) )
      {
        fclose($fp); // to match fopen()
        curl_close($ch); // to match curl_init()
        return "FAIL: curl_setopt(CURLOPT_URL)";
      }
      if ((!ini_get('open_basedir') && !ini_get('safe_mode')) || $redirects < 1) {
        curl_setopt($ch, CURLOPT_USERAGENT, '"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        //curl_setopt($ch, CURLOPT_REFERER, 'http://domain.com/');
        if( !curl_setopt($ch, CURLOPT_HEADER, $curlopt_header)) return "FAIL: curl_setopt(CURLOPT_HEADER)";
        if( !curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $redirects > 0)) return "FAIL: curl_setopt(CURLOPT_FOLLOWLOCATION)";
        if( !curl_setopt($ch, CURLOPT_FILE, $fp) ) return "FAIL: curl_setopt(CURLOPT_FILE)";
        if( !curl_setopt($ch, CURLOPT_MAXREDIRS, $redirects) ) return "FAIL: curl_setopt(CURLOPT_MAXREDIRS)";

        return curl_exec($ch);
    } else {
        curl_setopt($ch, CURLOPT_USERAGENT, '"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        //curl_setopt($ch, CURLOPT_REFERER, 'http://domain.com/');
        if( !curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false)) return "FAIL: curl_setopt(CURLOPT_FOLLOWLOCATION)";
        if( !curl_setopt($ch, CURLOPT_FILE, $fp) ) return "FAIL: curl_setopt(CURLOPT_FILE)";
        if( !curl_setopt($ch, CURLOPT_HEADER, true)) return "FAIL: curl_setopt(CURLOPT_HEADER)";
        if( !curl_setopt($ch, CURLOPT_RETURNTRANSFER, true)) return "FAIL: curl_setopt(CURLOPT_RETURNTRANSFER)";
        if( !curl_setopt($ch, CURLOPT_FORBID_REUSE, false)) return "FAIL: curl_setopt(CURLOPT_FORBID_REUSE)";
        curl_setopt($ch, CURLOPT_USERAGENT, '"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11');
    }
      // if( !curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true) ) return "FAIL: curl_setopt(CURLOPT_FOLLOWLOCATION)";
      // if( !curl_setopt($ch, CURLOPT_FILE, $fp) ) return "FAIL: curl_setopt(CURLOPT_FILE)";
      // if( !curl_setopt($ch, CURLOPT_HEADER, 0) ) return "FAIL: curl_setopt(CURLOPT_HEADER)";
      if( !curl_exec($ch) ) return "FAIL: curl_exec()";
      curl_close($ch);
      fclose($fp);
      return "SUCCESS: $file [$url]";
    }
    else return "FAIL: fopen()";
  }
  else return "FAIL: curl_init()";
}
Hoàng Vũ Tgtt
fonte
4

Uma solução PHP 4 e 5:

O readfile () não apresentará nenhum problema de memória, mesmo ao enviar arquivos grandes, por si só. Uma URL pode ser usada como um nome de arquivo com esta função se os wrappers fopen tiverem sido ativados.

http://php.net/manual/en/function.readfile.php

Eric Leroy
fonte
11
Isso não responde à pergunta, porque se trata de gravar no disco e não no buffer de saída.
Lorenz Meyer