简体   繁体   中英

How to download existing file in PHP

I have a pdf file on my server. I want to create such link that, user can click on it and download that pdf file. I am using Zend frame work with Php.

place this code inside a php file and call it fe "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;

?>

Example: place this kind of link into the document where the file download is offered:

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

More Detail:

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

I assume there is a reason you can't just link to the pdf directly, such as the need to authenticate the user. In that case, you will need to set the correct headers, and, as others have noted, you can use get_file_contents to serve the pdf. However, using get_file_contents requires that you read the file into memory before you send the response. If the file is large, or if you receive many requests at once, you can easily run out of memory. A great solution if you're using Apache or Lighttpd is to use XSendFile. With XSendFile you set the X-Sendfile response header to the file's path, and your web server serves the file directly from disk - without revealing the file's location on disk.

The problem with this solution is that the module must be installed on Apache, and it must be configured to work with Lighttpd.

Once XSendFile is installed, your Zend Framework action code would look something like the following:

// 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();
        header("Content-type: application/pdf");
        header("Content-Disposition: attachment; filename=filename.pdf"); 
        $pdfiledata = file_get_contents($filename);
        echo $pdfiledata;

I have been using this reusable action helper for sending files to users. It works well and is better than messing around with headers yourself.

I don't know if Zend provides a class for this. In general this is achieved by using the header function. have a look at the PHP site:

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

There are some examples of downloading files.

Good luck!

I've always used the become_file_download function from the open source BalPHP library which you can plug and play (stick) into your project right away. It allows for:

  • Instant download of the file
  • Easily specify params like content-type, cache life, buffer size.
  • Supports multi-part transactions allowing for faster downloads which won't kill your server for big file transfers.
  • Supports pause/resume functionality.
  • And etags for caching:

You can find the latest version here: http://github.com/balupton/balphp/blob/master/trunk/lib/core/functions/_files.funcs.php#L75

And here it is copy and pasted as of 27 August, 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 <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;
}

It also depends on another plug and play function called ensure_timestamp which you can find here: 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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM