Handle large file downloads in PHP

PHP Aug 06, 2020 Viewed 66 Comments 0

In order to handle file download, we need to response several HTTP headers and write a file to the output buffer.

1. Content-Type

Use finfo-file method to get the MIME-TYPE of a file.

$filePath = "data.zip";
$fInfo = finfo_open(FILEINFO_MIME_TYPE);
echo finfo_file($fInfo, $filePath);
finfo_close($fInfo);

Return the Content-Type header to inform the client of the file type.

header("Content-Type: application/zip");

2. Content-Disposition

In a regular HTTP response, the Content-Disposition response header is a header indicating if the content is expected to be displayed inline in the browser, that is, as a Web page or as part of a Web page, or as an attachment, that is downloaded and saved locally.

Content-Disposition: inline
Content-Disposition: attachment
Content-Disposition: attachment; filename="filename.jpg"

We specify Content-Disposition as the attachment and specify the file name of the attachment.

header("Content-Disposition: attachment; filename=\"data.zip\"");

3. Content-Length

Content-Length is used to indicate the size of the response content. For file download, you need to specify the size of the file.

$filePath = "data.zip";
$fileSize = filesize($filePath);
header("Content-Length: " . $fileSize);

4. Outputs a file

readfile is a simple way to ouput files files.

$filePath = "data.zip";
readfile($filePath);

But readfile() reads all the contents of the file into the memory, which is not a good way for large files. The simplest way to handle this is to output the file in "chunks".

$filePath = "data.zip";
$file = fopen($filePath, "r");
while (!feof($file)) {
    print(fread($file, 1024 * 8));
    ob_flush();
    flush();
}

Example

$filePath = "data.zip";
if (!file_exists($filePath)) {
    exit("file not exist");
}
$fInfo = finfo_open(FILEINFO_MIME_TYPE);
$type = finfo_file($fInfo, $filePath);
finfo_close($fInfo);
$fileSize = filesize($filePath);
header("Content-Type: " . $type);
header("Content-Disposition: attachment; filename=\"{$filePath}\"");
header("Content-Length: " . $fileSize);

$file = fopen($filePath, "r");
while (!feof($file)) {
    print(fread($file, 1024 * 8));
    ob_flush();
    flush();
}
Updated Aug 06, 2020