Télécharger le fichier sur le serveur à partir de l'URL

341

Eh bien, celui-ci semble assez simple, et il l'est. Tout ce que vous avez à faire pour télécharger un fichier sur votre serveur est:

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

Seulement, il y a un problème. Et si vous avez un gros fichier, comme 100 Mo. Ensuite, vous manquerez de mémoire et ne pourrez pas télécharger le fichier.

Ce que je veux, c'est un moyen d'écrire le fichier sur le disque pendant que je le télécharge. De cette façon, je peux télécharger des fichiers plus gros, sans rencontrer de problèmes de mémoire.

xaav
la source
4
Cela est défini dans la configuration de votre serveur, PHP ne peut pas vraiment le contourner pour autant que je sache (sauf pour une modification directe en .ini)
Ben

Réponses:

494

Depuis PHP 5.1.0, file_put_contents()prend en charge l'écriture pièce par pièce en passant un stream-handle comme $dataparamètre:

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

Du manuel:

Si data [qui est le deuxième argument] est une ressource de flux, le tampon restant de ce flux sera copié dans le fichier spécifié. Ceci est similaire à l'utilisation stream_copy_to_stream().

(Merci Hakre .)

alex
la source
4
Ce ne serait pas mon premier choix. Si allow_fopen_url Offest défini dans php.ini (bonne idée pour la sécurité), votre script serait cassé.
PleaseStand
4
@idealmachine je pense que file_get_contents()cela ne fonctionnerait pas non plus si c'était le cas (voir OP).
alex
10
@geoff J'étais précis, j'ai mentionné la fonction que vous vouliez. Vous souhaitiez peut-être que quelqu'un écrive le code pour vous, mais je suis sûr que vous avez appris quelque chose en le faisant vous-même. De plus, si nous allons commenter les interactions SO de chacun - veuillez accepter quelques réponses :)
alex
@alex: Veuillez consulter l'édition, n'hésitez pas à l'incorporer. faites-moi savoir quand je pourrai supprimer ce commentaire ici alors.
hakre
4
Le drapeau «b» doit également être utilisé dans la plupart des cas avec fopen; empêche les effets néfastes sur les images et autres fichiers de texte non ordinaire.
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);
    }
}
xaav
la source
1
merci pour votre extrait, mais pourriez-vous expliquer votre code @xaav? Je ne suis pas vraiment brillant en php. À quoi sert 1024 * 8? Merci encore.
vvMINOvv
@wMINOw La longueur de la ligne.
David Bélanger
2
Plus précisément, cela signifie de lire jusqu'à 8 Ko à la fois (1024 octets par Ko * 8) car le paramètre est en octets. Tant que la ligne est <= 8 Ko, il lira la ligne entière à la fois.
Doktor J
1
Pourquoi n'est-ce pas la meilleure réponse?
GunJack
1
Comment gérez-vous les erreurs avec cette approche? Que faire si un 404 est retourné ou si la connexion est interrompue ou arrive à expiration?
Adam Swinden
67

Essayez d'utiliser 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);

Je ne suis pas sûr mais je crois qu'avec l' CURLOPT_FILEoption qu'il écrit en tirant les données, c'est-à-dire. non tamponné.

prodigitalson
la source
2
Normalement, ce serait bien, mais j'ai ce code dans une application web, donc je ne peux pas être sûr que les utilisateurs auront installé cURL. Cependant, j'ai renoncé à voter.
xaav
@Geoff est-ce une application web distribuée? Parce que si vous contrôlez l'hébergement, cela n'a pas d'importance pour vos utilisateurs (cURL est une bibliothèque sur votre serveur).
alex
Non, je ne contrôle pas l'hébergement. Il s'agit d'une application Web distribuée que tout le monde pourrait avoir.
xaav
3
La boucle peut être manquante. Mais presque toutes les sociétés d'hébergement partagé ont installé CURL par défaut. Je veux dire, je n'en ai pas vu un qui ne marche pas.
Mangirdas Skripka
19
À partir de mes tests, vous ne pouvez pas affecter directement à CURLOPT_FILE un chemin de fichier. Il doit s'agir d'un gestionnaire de fichiers. Tout d'abord, ouvrez le fichier avec $fh = fopen('/path/to/download/the/file/to.zip', 'w');et fermez avec fclose($fh);après curl_close($ch);. Et setCURLOPT_FILE => $fh
Gustavo
22

La réponse de prodigitalson n'a pas fonctionné pour moi. J'ai missing fopen in CURLOPT_FILE plus de détails .

Cela a fonctionné pour moi, y compris les 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);
    }
}
Kamil Kiełczewski
la source
19
  1. Créer un dossier appelé "téléchargements" dans le serveur de destination
  2. Enregistrez [ce code] dans un .phpfichier et exécutez-le sur le serveur de destination

Téléchargeur:

<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
la source
Cela suppose que l'utilisateur souhaite un script autonome plutôt qu'une solution qui fonctionnera dans une application PHP existante, et je pense que cette dernière est ce que l'OP et la plupart des autres recherchent. Une explication serait également utile pour les personnes qui souhaitent comprendre l'approche.
Sean the Bean
1
chaque fois que j'essaye toujours ma taille de fichier transféré est 50816 mais ma taille de fichier est plus grande que cela .. 120 Mo .. Une idée pourquoi est-ce?
Riffaz Starr
set_time_limit (24 * 60 * 60);doit être mis dans une boucle. Cela n'a aucun effet au début du script.
Viktor Joras
16
set_time_limit(0); 
$file = file_get_contents('path of your file');
file_put_contents('file.ext', $file);
Dimmy
la source
votre réponse est très simple et fonctionne bien, m'a aidé où cURL n'a pas réussi à obtenir le fichier, cela a fonctionné. Merci :)
Tommix
2
Vous voudrez peut-être expliquer ce que cela fait réellement.
alex
6
Cela ne résout pas le problème de l'OP de dépasser la limite de mémoire PHP.
user9645
C'est assez simple et direct. Très utile pour les cas plus simples où les fichiers sont petits ou l'environnement est un développement local.
Valentine Shi
une idée pour les fichiers .xlsx? Il stocke un fichier vide avec une mémoire de 0 octet.
Dhruv Thakkar
9

Il y a 3 façons:

  1. file_get_contents et file_put_contents
  2. BOUCLE
  3. fopen

Vous pouvez trouver des exemples d'ici .

Hoan Huynh
la source
8

Utilisez une méthode simple en php copy()

copy($source_url, $local_path_with_file_name);

Remarque: si le fichier de destination existe déjà, il sera écrasé

Fonction PHP copy ()

Remarque: Vous devez définir l'autorisation 777 pour le dossier de destination. Utilisez cette méthode lorsque vous téléchargez sur votre ordinateur local.

Remarque spéciale: 777 est une autorisation dans un système basé sur Unix avec une autorisation complète de lecture / écriture / exécution pour le propriétaire, le groupe et tout le monde. En général, nous accordons cette autorisation aux actifs qui n'ont pas grand-chose à cacher au public sur un serveur Web. Exemple: dossier d'images.

Pradeep Kumar Prabaharan
la source
1
Je ne définirai jamais jamais 777 en tant que perms sur un serveur Web, et je lancerai tout développeur Web qui a la mauvaise idée de le faire. Tout le temps partout. Fais attention ! Tu ne peux pas faire ça ! Pensez à la sécurité. Suivre les règles OWASP n'est pas suffisant. Avoir une bonne réflexion sur des choses simples est important.
ThierryB
@ThierryB. Remarque: j'ai donné le chemin local. & cela peut être utilisé dans des applications internes. Avoir une bonne lecture et une bonne compréhension des questions et réponses. Pensez à différents scénarios. Et ce n'est pas accepté / meilleure réponse. Chaque question a des réponses différentes avec des avantages et des inconvénients. Exemple à comprendre: Même Fibonacci a plusieurs solutions uniques où une seule sera la meilleure. D'autres seront utilisés dans différents scénarios.
Pradeep Kumar Prabaharan
D'accord, mais prendre le temps de réfléchir aux meilleures pratiques et de les mettre en œuvre dans des endroits sécurisés vous permettra de mieux comprendre les concepts que vous devez mettre en œuvre. Peut-être que si un intrus est à l'intérieur de votre maison ($), en faisant des pièges ou en construisant les choses de la meilleure façon possible, cela lui donnera des maux de tête;)
ThierryB
5

J'utilise ceci pour télécharger le fichier

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
la source
4

Une solution PHP 4 & 5:

readfile () ne présentera aucun problème de mémoire, même lors de l'envoi de fichiers volumineux. Une URL peut être utilisée comme nom de fichier avec cette fonction si les wrappers fopen ont été activés.

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

Eric Leroy
la source
1
Cela ne répond pas à la question, car la question concerne l'écriture sur le disque et non dans le tampon de sortie.
Lorenz Meyer