【问题标题】:How to download existing file in PHP如何在 PHP 中下载现有文件
【发布时间】:2010-08-26 07:28:01
【问题描述】:

我的服务器上有一个 pdf 文件。我想创建这样的链接,用户可以单击它并下载该 pdf 文件。我正在将 Zend 框架与 Php 一起使用。

【问题讨论】:

  • 问题标题是多余的。我要如何下载一个不存在的文件?
  • @NullUserException:您可能希望在脚本执行时生成一个文件,将其单独保存在内存中;有人可能争辩说不是在下载“现有文件”。它肯定不会存在于文件系统上。 :)

标签: php zend-framework download


【解决方案1】:

将此代码放在一个 php 文件中并命名为 f.e. “下载.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;

?>

示例:将此类链接放入提供文件下载的文档中:

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

更多详情:

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

【讨论】:

    【解决方案2】:

    我认为您不能直接链接到 pdf 是有原因的,例如需要对用户进行身份验证。在这种情况下,您将需要设置正确的标题,并且正如其他人所指出的,您可以使用 get_file_contents 来提供 pdf。但是,使用 get_file_contents 要求您在发送响应之前将文件读入内存。如果文件很大,或者一次收到很多请求,很容易耗尽内存。如果您使用 Apache 或 Lighttpd,一个很好的解决方案是使用 XSendFile。使用 XSendFile,您可以将 X-Sendfile 响应标头设置为文件的路径,然后您的 Web 服务器直接从磁盘提供文件 - 而不会透露文件在磁盘上的位置。

    此解决方案的问题是该模块必须安装在 Apache 上,并且必须配置为与 Lighttpd 一起使用。

    安装 XSendFile 后,您的 Zend Framework 操作代码将如下所示:

    // 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();
    

    【讨论】:

      【解决方案3】:
              header("Content-type: application/pdf");
              header("Content-Disposition: attachment; filename=filename.pdf"); 
              $pdfiledata = file_get_contents($filename);
              echo $pdfiledata;
      

      【讨论】:

      【解决方案4】:

      我一直在使用this 可重复使用的操作助手向用户发送文件。它工作得很好,比自己乱搞标题要好。

      【讨论】:

        【解决方案5】:

        我不知道 Zend 是否为此提供了一个类。通常,这是通过使用 header 函数来实现的。看看 PHP 网站:

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

        有一些下载文件的例子。

        祝你好运!

        【讨论】:

          【解决方案6】:

          我一直使用开源 BalPHP 库中的 become_file_download 函数,您可以立即将其插入到您的项目中。它允许:

          • 文件即时下载
          • 轻松指定内容类型、缓存寿命、缓冲区大小等参数。
          • 支持多部分事务,允许更快的下载,不会因为大文件传输而杀死您的服务器。
          • 支持暂停/恢复功能。
          • 以及用于缓存的 etag:

          您可以在此处找到最新版本: http://github.com/balupton/balphp/blob/master/trunk/lib/core/functions/_files.funcs.php#L75

          这里是 2010 年 8 月 27 日的复制和粘贴:

          /**
           * 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 <contact@balupton.com> - {@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;
          }
          

          它还依赖于另一个名为ensure_timestamp 的即插即用功能,您可以在此处找到: 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;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-07-09
            • 1970-01-01
            • 2010-09-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多