2010-08-26 13 views
4

J'ai un fichier pdf sur mon serveur. Je veux créer un tel lien que, l'utilisateur peut cliquer dessus et télécharger ce fichier pdf. J'utilise le travail de cadre de Zend avec Php.Comment télécharger un fichier existant en PHP

+1

Le titre de la question est superflu. Comment vais-je télécharger un fichier qui n'existe pas? – NullUserException

+1

@NullUserException: Vous souhaiterez peut-être générer un fichier à l'exécution du script, en le maintenant uniquement en mémoire; Un * pourrait * argumenter qu'il ne télécharge pas un 'fichier existant'. Cela n'existerait certainement pas sur le système de fichiers. :) – pinkgothic

Répondre

4

Placez ce code dans un fichier php et appelez-le f.e. "Download.php":

<?php 

$fullPath = "path/to/your/file.ext"; 

if ($fd = fopen ($fullPath, "r")) { 

    $fsize = filesize($fullPath); 
    $path_parts = pathinfo($fullPath); 
    $ext = strtolower($path_parts["extension"]); 

    header("Content-type: application/pdf"); 
    header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\"");    
    header("Content-length: $fsize"); 
    header("Cache-control: private"); 

    while(!feof($fd)) { 
     $buffer = fread($fd, 2048); 
     echo $buffer; 
    } 
} 

fclose ($fd); 
exit; 

?> 

Exemple: placer ce type de lien dans le document où le téléchargement de fichier est offert:

<a href="download.php?download_file=some_file.pdf">Download here</a> 

Plus de détail:

http://www.finalwebsites.com/forums/topic/php-file-download

1

Je ne sais pas si Zend fournit une classe pour cela. En général, ceci est réalisé en utilisant la fonction d'en-tête. jetez un oeil sur le site PHP:

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

Il existe quelques exemples de téléchargement de fichiers.

Bonne chance!

2
 header("Content-type: application/pdf"); 
     header("Content-Disposition: attachment; filename=filename.pdf"); 
     $pdfiledata = file_get_contents($filename); 
     echo $pdfiledata; 
+0

Court, concis et précis, bien que je préfère utiliser 'fpassthru' http://us3.php.net/manual/fr/function.fpassthru.php que' file_get_contents' http: //us3.php .net/manual/fr/function.file-get-contents.php –

+0

hors-sujet: je vous suggérerais plus à propos de fpassthru. est-ce plus sûr? Je ne l'ai jamais su. – thevikas

+1

Ceci est horrible pour la performance, en vidant le contenu du fichier en mémoire. J'aurais aimé avoir des downvotes à gauche. – NullUserException

2

J'utilise l'assistant d'action réutilisable this pour envoyer des fichiers aux utilisateurs. Cela fonctionne bien et c'est mieux que de se débrouiller avec les en-têtes.

4

Je suppose qu'il y a une raison pour laquelle vous ne pouvez pas simplement lier directement au pdf, comme la nécessité d'authentifier l'utilisateur. Dans ce cas, vous devrez définir les en-têtes appropriés, et, comme d'autres l'ont noté, vous pouvez utiliser get_file_contents pour servir le pdf. Cependant, l'utilisation de get_file_contents nécessite que vous lisiez le fichier en mémoire avant d'envoyer la réponse. Si le fichier est volumineux ou si vous recevez plusieurs demandes à la fois, vous pouvez facilement manquer de mémoire. Une excellente solution si vous utilisez Apache ou Lighttpd est d'utiliser XSendFile. Avec XSendFile, vous définissez l'en-tête de réponse X-Sendfile sur le chemin du fichier, et votre serveur Web sert le fichier directement à partir du disque - sans révéler l'emplacement du fichier sur le disque.

Le problème avec cette solution est que le module doit être installé sur Apache, et il doit être configuré pour fonctionner avec Lighttpd.

Une fois XSendFile est installé, votre code d'action Zend Framework ressemblerait à quelque chose comme ce qui suit:

// user auth or other code here -- there has to be a reason you're not 
// just pointing to the pdf file directly 

$this->_helper->layout->disableLayout(); 
$this->_helper->viewRenderer->setNoRender(); 

$this->getResponse()->setHeader('Content-type', 'application/pdf') 
        ->setHeader('X-Sendfile', 'path-to-file') 
        ->sendResponse(); 
1

J'ai toujours utilisé la fonction become_file_download de la bibliothèque source BalPHP ouverte que vous pouvez brancher et jouer (bâton) dans votre projet tout de suite. Il permet:

  • téléchargement instantané du fichier
  • facilement spécifier params comme type de contenu, la vie du cache, la taille de la mémoire tampon.
  • Prend en charge les transactions en plusieurs parties permettant des téléchargements plus rapides qui ne tueront pas votre serveur pour les gros transferts de fichiers.
  • Prend en charge la fonctionnalité de pause/reprise.
  • Et ETAG pour la mise en cache:

Vous pouvez trouver la dernière version ici: http://github.com/balupton/balphp/blob/master/trunk/lib/core/functions/_files.funcs.php#L75

Et là, il est exemplaire et collé en date du 27 Août 2010:

/** 
* Become a file download, should be the last script that runs in your program 
* 
* http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt 
* 
* @version 3, July 18, 2009 (Added suport for data) 
* @since 2, August 11, 2007 
* 
* @author Benjamin "balupton" Lupton <[email protected]> - {@link http://www.balupton.com/} 
* 
* @param string $file_path 
* @param string $content_type 
* @param int  $buffer_size 
* @param string $file_name 
* @param timestamp $file_time 
* 
* @return boolean true on success, false on error 
*/ 
function become_file_download ($file_path_or_data, $content_type = NULL, $buffer_size = null, $file_name = null, $file_time = null, $expires = null) { 

    // Prepare 
    if (empty($buffer_size)) 
     $buffer_size = 4096; 
    if (empty($content_type)) 
     $content_type = 'application/force-download'; 

    // Check if we are data 
    $file_descriptor = null; 
    if (file_exists($file_path_or_data) && $file_descriptor = fopen($file_path_or_data, 'rb')) { 
     // We could be a file 
     // Set some variables 
     $file_data = null; 
     $file_path = $file_path_or_data; 
     $file_name = $file_name ? $file_name : basename($file_path); 
     $file_size = filesize($file_path); 
     $file_time = filemtime($file_path); 
     $etag = md5($file_time . $file_name); 
    } elseif ($file_name !== null) { 
     // We are just data 
     $file_data = $file_path_or_data; 
     $file_path = null; 
     $file_size = strlen($file_data); 
     $etag = md5($file_data); 
     if ($file_time === null) 
      $file_time = time(); 
     else 
      $file_time = ensure_timestamp($file_time); 
    } else { 
     // We couldn't find the file 
     header('HTTP/1.1 404 Not Found'); 
     return false; 
    } 

    // Prepare timestamps 
    $expires = ensure_timestamp($expires); 

    // Set some variables 
    $date = gmdate('D, d M Y H:i:s') . ' GMT'; 
    $expires = gmdate('D, d M Y H:i:s', $expires) . ' GMT'; 
    $last_modified = gmdate('D, d M Y H:i:s', $file_time) . ' GMT'; 

    // Say we can go on forever 
    set_time_limit(0); 

    // Check relevance 
    $etag_relevant = !empty($_SERVER['HTTP_IF_NONE_MATCH']) && trim(stripslashes($_SERVER['HTTP_IF_NONE_MATCH']), '\'"') === $etag; 
    $date_relevant = !empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $file_time; 

    // Handle download 
    if ($etag_relevant || $date_relevant) { 
     // Not modified 
     header('HTTP/1.0 304 Not Modified'); 
     header('Status: 304 Not Modified'); 

     header('Pragma: public'); 
     header('Cache-Control: private'); 

     header('ETag: "' . $etag . '"'); 
     header('Date: ' . $date); 
     header('Expires: ' . $expires); 
     header('Last-modified: ' . $last_modified); 
     return true; 
    } elseif (!empty($_SERVER['HTTP_RANGE'])) { 
     // Partial download 


     /* 
     * bytes=0-99,500-1499,4000- 
     */ 

     // Explode RANGE 
     list($size_unit,$ranges) = explode($_SERVER['HTTP_RANGE'], '=', 2); 

     // Explode RANGES 
     $ranges = explode(',', $ranges); 

     // Cycle through ranges 
     foreach ($ranges as $range) { 
      // We have a range 


      /* 
      * All bytes until the end of document, except for the first 500 bytes: 
      * Content-Range: bytes 500-1233/1234 
      */ 

      // Set range start 
      $range_start = null; 
      if (!empty($range[0]) && is_numeric($range[0])) { 
       // The range has a start 
       $range_start = intval($range[0]); 
      } else { 
       $range_start = 0; 
      } 

      // Set range end 
      if (!empty($range[1]) && is_numeric($range[1])) { 
       // The range has an end 
       $range_end = intval($range[1]); 
      } else { 
       $range_end = $file_size - 1; 
      } 

      // Set the range size 
      $range_size = $range_end - $range_start + 1; 

      // Set the headers 
      header('HTTP/1.1 206 Partial Content'); 

      header('Pragma: public'); 
      header('Cache-Control: private'); 

      header('ETag: "' . $etag . '"'); 
      header('Date: ' . $date); 
      header('Expires: ' . $expires); 
      header('Last-modified: ' . $last_modified); 

      header('Content-Transfer-Encoding: binary'); 
      header('Accept-Ranges: bytes'); 

      header('Content-Range: bytes ' . $range_start . '-' . $range_end . '/' . $file_size); 
      header('Content-Length: ' . $range_size); 

      header('Content-Type: ' . $content_type); 
      if ($content_type === 'application/force-download') 
       header('Content-Disposition: attachment; filename=' . urlencode($file_name)); 

      // Handle our data transfer 
      if (!$file_path) { 
       // We are using file_data 
       echo substr($file_data, $range_start, $range_end - $range_start); 
      } else { 
       // Seek to our location 
       fseek($file_descriptor, $range_start); 

       // Read the file 
       $remaining = $range_size; 
       while ($remaining > 0) { 
        // 0-6 | buffer = 3 | remaining = 7 
        // 0,1,2 | buffer = 3 | remaining = 4 
        // 3,4,5 | buffer = 3 | remaining = 1 
        // 6  | buffer = 1 | remaining = 0 


        // Set buffer size 
        $buffer_size = min($buffer_size, $remaining); 

        // Output file contents 
        echo fread($file_descriptor, $buffer_size); 
        flush(); 
        ob_flush(); 

        // Update remaining 
        $remaining -= $buffer_size; 
       } 
      } 
     } 
    } else { 
     // Usual download 


     // header('Pragma: public'); 
     // header('Cache-control: must-revalidate, post-check=0, pre-check=0'); 
     // header('Expires: '.  gmdate('D, d M Y H:i:s').' GMT'); 


     // Set headers 
     header('HTTP/1.1 200 OK'); 

     header('Pragma: public'); 
     header('Cache-Control: private'); 

     header('ETag: "' . $etag . '"'); 
     header('Date: ' . $date); 
     header('Expires: ' . $expires); 
     header('Last-modified: ' . $last_modified); 

     header('Content-Transfer-Encoding: binary'); 
     header('Accept-Ranges: bytes'); 

     header('Content-Length: ' . $file_size); 

     header('Content-Type: ' . $content_type); 
     if ($content_type === 'application/force-download') 
      header('Content-Disposition: attachment; filename=' . urlencode($file_name)); 

     // Handle our data transfer 
     if (!$file_path) { 
      // We are using file_data 
      echo $file_data; 
     } else { 
      // Seek to our location 
      // Read the file 
      $file_descriptor = fopen($file_path, 'r'); 
      while (!feof($file_descriptor)) { 
       // Output file contents 
       echo fread($file_descriptor, $buffer_size); 
       flush(); 
       ob_flush(); 
      } 
     } 
    } 

    // Close the file 
    if ($file_descriptor) 
     fclose($file_descriptor); 

    // Done 
    return true; 
} 

Il dépend également d'une autre fonction plug and play appelée ensure_timestamp que vous pouvez trouver ici: http://github.com/balupton/balphp/blob/master/trunk/lib/core/functions/_datetime.funcs.php#L31

/** 
* Gets the days between two timestamps 
* @version 1, January 28, 2010 
* @param mixed $value 
* @return timestamp 
*/ 
function ensure_timestamp ($value = null) { 
    $result = null; 

    if ($value === null) $result = time(); 
    elseif (is_numeric($value)) $result = $value; 
    elseif (is_string($value)) $result = strtotime($value); 
    else throw new Exception('Unknown timestamp type.'); 

    return $result; 
} 
Questions connexes