如何在PHP中下载现有文件

时间:2010-08-26 07:28:02

标签: php zend-framework download

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

6 个答案:

答案 0 :(得分:4)

将此代码放在php文件中并将其命名为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;

?>

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

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

更多细节:

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

答案 1 :(得分:4)

我认为您不能直接链接到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();

答案 2 :(得分:2)

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

答案 3 :(得分:2)

我一直在使用this可重复使用的操作助手向用户发送文件。它运作良好,并且比自己搞乱标题更好。

答案 4 :(得分:1)

我不知道Zend是否为此提供了一个类。通常,这是通过使用头功能实现的。看一下PHP网站:

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

有一些下载文件的例子。

祝你好运!

答案 5 :(得分:1)

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

  • 即时下载文件
  • 轻松指定内容类型,缓存时长,缓冲区大小等参数。
  • 支持多部分交易,允许更快的下载,不会因为大文件传输而终止服务器。
  • 支持暂停/恢复功能。
  • 用于缓存的etags:

您可以在这里找到最新版本: 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;
}