Generally speaking, we can guide users to download files by directly pointing to a file located under Document Root.
However, if you do this, you can't do some statistics, permission checking, etc. So, many times, we use PHP to forward and provide users with file downloads.
<?php
$file = "/tmp/";
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header("Content-Length: ". filesize($file));
readfile($file);
But there is a problem with this, that is, if the file is in Chinese, some users may have garbled file names after downloading.
So, let's make some modifications:
<?php
$file = "/tmp/Chinese name.";
$filename = basename($file);
header("Content-type: application/octet-stream");
//Processing Chinese file names
$ua = $_SERVER["HTTP_USER_AGENT"];
$encoded_filename = rawurlencode($filename);
if (preg_match("/MSIE/", $ua)) {
header('Content-Disposition: attachment; filename="' . $encoded_filename . '"');
} else if (preg_match("/Firefox/", $ua)) {
header("Content-Disposition: attachment; filename*=\"utf8''" . $filename . '"');
} else {
header('Content-Disposition: attachment; filename="' . $filename . '"');
}
header("Content-Length: ". filesize($file));
readfile($file);
Well, it looks much better now, but there is another problem, that is, readfile. Although PHP's readfile tries to be as efficient as possible and does not occupy PHP's own memory, it actually still needs to use MMAP (if supported), or a fixed buffer to loop through the file and output directly.
When outputting, if it is Apache + PHP mod, it also needs to be sent to Apache's output buffer. It is finally sent to the user. For Nginx + fpm, if they are deployed separately, it will also bring additional network IO.
So, can we directly let the Webserver send files to the user without going through the PHP layer?
Today, I saw an interesting article: How I PHP: X-SendFile.
We can use Apache's module mod_xsendfile to let Apache send this file directly to the user:
<?php
$file = "/tmp/Chinese name.";
$filename = basename($file);
header("Content-type: application/octet-stream");
//Processing Chinese file names
$ua = $_SERVER["HTTP_USER_AGENT"];
$encoded_filename = rawurlencode($filename);
if (preg_match("/MSIE/", $ua)) {
header('Content-Disposition: attachment; filename="' . $encoded_filename . '"');
} else if (preg_match("/Firefox/", $ua)) {
header("Content-Disposition: attachment; filename*=\"utf8''" . $filename . '"');
} else {
header('Content-Disposition: attachment; filename="' . $filename . '"');
}
//Let Xsendfile send file
header("X-Sendfile: $file");
The X-Sendfile header will be processed by Apache and send the response file directly to the Client.
Lighttpd and Nginx also have similar modules. If you are interested, you can search for them.