Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

apache - Downloading files with download.php

I need to deliver big files like file.zip (~2 GB) to customers, with a unique URL for each customer. Then I will redirect (with .htaccess) a customer download link example.com/download/f6zDaq/file.zip to something like

example.com/download.php?id=f6zDaq&file=file.zip

But as the files are big, I don't want the fact that PHP processes the downloading (instead of just letting Apache handle it) to be a CPU / RAM performance issue for my server. After all, asking PHP to do it involves a new layer, so it might cause such an issue, if not done properly.

Question: among the following solutions, which one(s) are the best practice? (in particular, in terms of CPU/RAM)?

  • 1: PHP solution with application/download

    header('Content-Type: application/download');
    header('Content-Disposition: attachment; filename=file.zip');
    readfile("/path/to/file.zip");
    

    CPU usage measured while downloading: 13.6%.

  • 1bis: PHP solution with application/octet-stream (coming from Example #1 of this page)

    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename=file.zip');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize('file.zip'));
    readfile("/path/to/file.zip");
    
  • 1ter: PHP solution with application/octet-stream (coming from here):

    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename=file.zip'); 
    header('Content-Transfer-Encoding: binary'); // additional line
    header('Connection: Keep-Alive');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); // additional line
    header('Pragma: public');
    header('Content-Length: ' . filesize('file.zip'));
    readfile("/path/to/file.zip");
    
  • 1quater: Another PHP variant with application/force-download (edited; coming from here):

    header("Content-Disposition: attachment; filename=file.zip");
    header("Content-Type: application/force-download");
    header("Content-Length: " . filesize($file));
    header("Connection: close");
    
  • 2: Apache solution, no PHP involved: let Apache serve the file, and use .htaccess to provide different URL for the same file (many ways to do it can be written). In terms of performance, it's similar to let the customer download example.com/file.zip, served by Apache server.

  • 3: Another PHP solution. This would probably work:

    $myfile = file_get_contents("file.zip");
    echo $myfile;
    

    but wouldn't this ask PHP to load the whole content in memory? (which would be bad in terms of performance!)

  • 4: Just do a header("Location: /abcd/file.zip"); redirection as explained in File with a short URL downloaded with original filename.

    Problem with this solution: this discloses the actual location of the file

     example.com/abcd/file.zip
    

    to the end user (who can then use or share this URL without authentification) which is not wanted...

    But on the other hand, it is much lighter for the CPU since PHP just redirects the request and doesn't deliver the file itself.

    CPU usage measured while downloading: 10.6%.


Note: the readfile doc says:

readfile() will not present any memory issues, even when sending large files, on its own. If you encounter an out of memory error ensure that output buffering is off with ob_get_level().

but I wanted to be 100% sure that it won't be slower / more CPU/RAM hungry than pure Apache solution.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You could use .htaccess to redirect the request to the file while keeping the permalink structure:

RewriteEngine On
RewriteBase /
RewriteRule ^download/([^/]+)/file.zip download.php?id=$1 [L,NC]

Then in your download.php, you can check if the provided id is valid:

// Path to file
$file = 'file.zip';

// If the ID is valid
if ($condition) {
    header("Content-Disposition: attachment; filename="" . basename($file) . """);
    header("Content-Type: application/force-download");
    header("Content-Length: " . filesize($file));
    header("Connection: close");
} else {
    // Handle invalid ids
    header('Location: /');
}

When the user visits a valid url http://example.com/download/f6zDaq/file.zip, the download will start and the connection will be closed.

If the user visits an invalid url, they will be redirected to the home page.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...