Descargar archivo al servidor desde URL


341

Bueno, este parece bastante simple, y lo es. Todo lo que tiene que hacer para descargar un archivo a su servidor es:

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

Solo hay un problema. ¿Qué pasa si tiene un archivo grande, como 100mb? Luego, se quedará sin memoria y no podrá descargar el archivo.

Lo que quiero es una forma de escribir el archivo en el disco mientras lo descargo. De esa manera, puedo descargar archivos más grandes, sin tener problemas de memoria.


44
Eso está configurado en la configuración de su servidor, PHP no puede evitarlo hasta donde yo sé (a excepción de una edición directa de .ini)
Ben

Respuestas:


494

Desde PHP 5.1.0, file_put_contents()admite escribir pieza por pieza pasando un identificador de flujo como $dataparámetro:

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

Del manual:

Si los datos [ese es el segundo argumento] es un recurso de secuencia, el búfer restante de esa secuencia se copiará en el archivo especificado. Esto es similar con el uso stream_copy_to_stream().

(Gracias Hakre )


44
Esa no sería mi primera opción. Si allow_fopen_url Offestá configurado en php.ini (buena idea para la seguridad), su secuencia de comandos se rompería.
favor

44
@idealmachine Creo file_get_contents()que tampoco funcionaría si ese fuera el caso (ver OP).
alex

10
@geoff Era específico, mencioné la función que querías. Lo que quizás quisiste fue que alguien te escribiera el código, pero estoy seguro de que aprendiste algo haciéndolo tú mismo. Además, si vamos a comentar las interacciones SO de los demás , acepte algunas respuestas más :)
alex

@alex: Por favor vea la edición, siéntase libre de incorporar. avísame cuando pueda eliminar este comentario aquí.
Hakre

44
La bandera 'b' también debe usarse en la mayoría de los casos con fopen; evita efectos adversos a las imágenes y otros archivos de texto no planos.
Wayne Weibel

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

1
gracias por su snippit, pero ¿podría explicar su código @xaav? No soy exactamente brillante en PHP. ¿Para qué es 1024 * 8? Gracias de nuevo.
vvMINOvv

@wMINOw La longitud de la línea.
David Bélanger

2
Específicamente, significa leer hasta 8 KB a la vez (1024 bytes por KB * 8) ya que el parámetro está en bytes. Mientras la línea sea <= 8 KB, leerá toda la línea a la vez.
Doktor J

1
¿Por qué no es esta la mejor respuesta?
GunJack

1
¿Cómo manejas los errores con este enfoque? ¿Qué sucede si se devuelve un 404 o se interrumpe la conexión o se agota el tiempo de espera?
Adam Swinden

67

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

No estoy seguro, pero creo que con la CURLOPT_FILEopción que escribe mientras extrae los datos, es decir. No tamponado.


2
Normalmente, esto estaría bien, pero tengo este código en una aplicación web, por lo que no puedo estar seguro de que los usuarios tengan instalado cURL. Sin embargo, le di un voto a esto.
xaav

@ Geoff, ¿es una aplicación web distribuida? Porque si controlas el alojamiento, entonces no importa tus usuarios (cURL es una biblioteca en tu servidor).
alex

No. No controlo el hosting. Es una aplicación web distribuida que cualquiera podría tener.
xaav

3
Curl podría faltar. Pero casi todas las empresas de alojamiento compartido tienen CURL instalado de forma predeterminada. Quiero decir, no he visto uno que no.
Mangirdas Skripka

19
A partir de mis pruebas, no puede asignar a CURLOPT_FILE una ruta de archivo directamente. Tiene que ser un controlador de archivos. Primero, abra el archivo con $fh = fopen('/path/to/download/the/file/to.zip', 'w');y cierre con fclose($fh);after curl_close($ch);. Y listoCURLOPT_FILE => $fh
Gustavo,

22

La respuesta de prodigitalson no funcionó para mí. Tengo missing fopen in CURLOPT_FILE más detalles .

Esto funcionó para mí, incluidas las URL locales:

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

19
  1. Cree una carpeta llamada "descargas" en el servidor de destino
  2. Guarde [este código] en el .phparchivo y ejecútelo en el servidor de destino

Descargador:

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

Esto supone que el usuario quiere un script independiente en lugar de una solución que funcione dentro de una aplicación PHP existente, y creo que este último es lo que el OP y la mayoría de los demás están buscando. Una explicación también sería útil para las personas que desean comprender el enfoque.
Sean the Bean

1
cada vez que intento esto, mi tamaño de archivo transferido es 50816 pero mi tamaño de archivo es mayor que esto ... 120 MB ... ¿Alguna idea de por qué es esto?
Riffaz Starr

set_time_limit (24 * 60 * 60);tiene que ser puesto dentro de un bucle. No tiene ningún efecto al comienzo del guión.
Viktor Joras

16
set_time_limit(0); 
$file = file_get_contents('path of your file');
file_put_contents('file.ext', $file);

su respuesta es muy simple y buen trabajo, me ayudó donde cURL no pudo obtener el archivo, esto funcionó. Gracias :)
Tommix

2
Es posible que desee explicar lo que esto realmente hace.
alex

66
Esto no soluciona el problema del OP de exceder el límite de memoria PHP.
user9645

Esto es bastante simple y directo. Bastante útil para casos más simples donde los archivos son pequeños o el entorno es un desarrollo local.
Valentine Shi

alguna idea para archivos .xlsx? Está almacenando un archivo vacío con 0 bytes de memoria.
Dhruv Thakkar


8

Use un método simple en php copy()

copy($source_url, $local_path_with_file_name);

Nota: si el archivo de destino ya existe, se sobrescribirá

Función PHP copy ()

Nota: debe establecer el permiso 777 para la carpeta de destino. Use este método cuando realice descargas en su máquina local.

Nota especial: 777 es un permiso en un sistema basado en Unix con permiso completo de lectura / escritura / ejecución para el propietario, el grupo y todos. En general, otorgamos este permiso a los activos que no son muy necesarios para ocultarse del público en un servidor web. Ejemplo: carpeta de imágenes.


1
Nunca nunca estableceré 777 como permisos permanentes en un servidor web, e iniciaré a cualquier desarrollador web que tenga la mala idea de hacerlo. Todo el tiempo en todo lugar. Ten cuidado ! Usted no puede hacer eso ! Piensa en la seguridad. Seguir las reglas de OWASP no es suficiente. Tener buen pensamiento sobre cosas simples es importante.
ThierryB

@ThierryB. Nota: he dado ruta local. & esto se puede usar en aplicaciones internas. Tener una buena lectura y comprensión de las preguntas y respuestas es importante. Piensa en diferentes escenarios. Y esto no es aceptado / mejor respuesta. Cada pregunta tiene diferentes respuestas con pros y contras. Ejemplo para que entiendas: Incluso Fibonacci tiene múltiples soluciones únicas donde solo una será la mejor. Otros serán utilizados en diferentes escenarios.
Pradeep Kumar Prabaharan

De acuerdo, pero tomarse el tiempo para pensar en las mejores prácticas e implementarlas en lugares seguros le dará una mejor comprensión de los conceptos que debe implementar. Tal vez si un intruso está dentro de su casa ($), haciendo algunas trampas o construyendo cosas de la mejor manera que pueda le dará algunos dolores de cabeza;)
ThierryB

5

Lo uso para descargar el archivo

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

4

Una solución PHP 4 y 5:

readfile () no presentará ningún problema de memoria, incluso al enviar archivos grandes, por sí solo. Se puede usar una URL como nombre de archivo con esta función si se han habilitado los contenedores fopen.

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


1
Esto no responde la pregunta, porque la pregunta es sobre escribir en el disco, no en el búfer de salida.
Lorenz Meyer
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.