文件下载的 HTTP 标头
我编写了一个 PHP 脚本来处理文件下载,确定正在请求哪个文件并设置正确的 HTTP 标头以触发浏览器实际下载文件(而不是在浏览器中显示它)。
我现在遇到一个问题,一些用户报告某些文件被错误识别(因此无论扩展名如何,浏览器都会将其视为 GIF 图像)。 我猜这是因为我没有在响应标头中设置“内容类型”。 这最有可能是这种情况吗? 如果是这样,是否有一个相当通用的类型可以用于所有文件,而不是尝试考虑每种可能的文件类型?
目前,我仅设置值“Content-disposition:attachment; filename=arandomf.ile”
更新:我在此处遵循本指南来构建更强大的文件下载流程(http://w-shadow.com/blog /2007/08/12/how-to-force-file-download-with-php/),但是执行脚本和浏览器下载对话框出现之间存在明显的延迟。 任何人都可以找出导致此问题的瓶颈吗?
这是我的实现:
/**
* Outputs the specified file to the browser.
*
* @param string $filePath the path to the file to output
* @param string $fileName the name of the file
* @param string $mimeType the type of file
*/
function outputFile($filePath, $fileName, $mimeType = '') {
// Setup
$mimeTypes = array(
'pdf' => 'application/pdf',
'txt' => 'text/plain',
'html' => 'text/html',
'exe' => 'application/octet-stream',
'zip' => 'application/zip',
'doc' => 'application/msword',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'gif' => 'image/gif',
'png' => 'image/png',
'jpeg' => 'image/jpg',
'jpg' => 'image/jpg',
'php' => 'text/plain'
);
$fileSize = filesize($filePath);
$fileName = rawurldecode($fileName);
$fileExt = '';
// Determine MIME Type
if($mimeType == '') {
$fileExt = strtolower(substr(strrchr($filePath, '.'), 1));
if(array_key_exists($fileExt, $mimeTypes)) {
$mimeType = $mimeTypes[$fileExt];
}
else {
$mimeType = 'application/force-download';
}
}
// Disable Output Buffering
@ob_end_clean();
// IE Required
if(ini_get('zlib.output_compression')) {
ini_set('zlib.output_compression', 'Off');
}
// Send Headers
header('Content-Type: ' . $mimeType);
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Content-Transfer-Encoding: binary');
header('Accept-Ranges: bytes');
// Send Headers: Prevent Caching of File
header('Cache-Control: private');
header('Pragma: private');
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
// Multipart-Download and Download Resuming Support
if(isset($_SERVER['HTTP_RANGE'])) {
list($a, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
list($range) = explode(',', $range, 2);
list($range, $rangeEnd) = explode('-', $range);
$range = intval($range);
if(!$rangeEnd) {
$rangeEnd = $fileSize - 1;
}
else {
$rangeEnd = intval($rangeEnd);
}
$newLength = $rangeEnd - $range + 1;
// Send Headers
header('HTTP/1.1 206 Partial Content');
header('Content-Length: ' . $newLength);
header('Content-Range: bytes ' . $range - $rangeEnd / $fileSize);
}
else {
$newLength = $fileSize;
header('Content-Length: ' . $fileSize);
}
// Output File
$chunkSize = 1 * (1024*1024);
$bytesSend = 0;
if($file = fopen($filePath, 'r')) {
if(isset($_SERVER['HTTP_RANGE'])) {
fseek($file, $range);
while(!feof($file) && !connection_aborted() && $bytesSend < $newLength) {
$buffer = fread($file, $chunkSize);
echo $buffer;
flush();
$bytesSend += strlen($buffer);
}
fclose($file);
}
}
}
I've written a PHP script that handles file downloads, determining which file is being requested and setting the proper HTTP headers to trigger the browser to actually download the file (rather than displaying it in the browser).
I now have a problem where some users have reported certain files being identified incorrectly (so regardless of extension, the browser will consider it a GIF image). I'm guessing this is because I haven't set the "Content-type" in the response header. Is this most likely the case? If so, is there a fairly generic type that could be used for all files, rather than trying to account for every possible file type?
Currently I'm only setting the value "Content-disposition: attachment; filename=arandomf.ile"
Update: I followed this guide here to build a more robust process for file downloads (http://w-shadow.com/blog/2007/08/12/how-to-force-file-download-with-php/), but there is a significant delay between when the script is executed and when the browser's download dialog appears. Can anyone identify the bottleneck that is causing this?
Here's my implementation:
/**
* Outputs the specified file to the browser.
*
* @param string $filePath the path to the file to output
* @param string $fileName the name of the file
* @param string $mimeType the type of file
*/
function outputFile($filePath, $fileName, $mimeType = '') {
// Setup
$mimeTypes = array(
'pdf' => 'application/pdf',
'txt' => 'text/plain',
'html' => 'text/html',
'exe' => 'application/octet-stream',
'zip' => 'application/zip',
'doc' => 'application/msword',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'gif' => 'image/gif',
'png' => 'image/png',
'jpeg' => 'image/jpg',
'jpg' => 'image/jpg',
'php' => 'text/plain'
);
$fileSize = filesize($filePath);
$fileName = rawurldecode($fileName);
$fileExt = '';
// Determine MIME Type
if($mimeType == '') {
$fileExt = strtolower(substr(strrchr($filePath, '.'), 1));
if(array_key_exists($fileExt, $mimeTypes)) {
$mimeType = $mimeTypes[$fileExt];
}
else {
$mimeType = 'application/force-download';
}
}
// Disable Output Buffering
@ob_end_clean();
// IE Required
if(ini_get('zlib.output_compression')) {
ini_set('zlib.output_compression', 'Off');
}
// Send Headers
header('Content-Type: ' . $mimeType);
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Content-Transfer-Encoding: binary');
header('Accept-Ranges: bytes');
// Send Headers: Prevent Caching of File
header('Cache-Control: private');
header('Pragma: private');
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
// Multipart-Download and Download Resuming Support
if(isset($_SERVER['HTTP_RANGE'])) {
list($a, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
list($range) = explode(',', $range, 2);
list($range, $rangeEnd) = explode('-', $range);
$range = intval($range);
if(!$rangeEnd) {
$rangeEnd = $fileSize - 1;
}
else {
$rangeEnd = intval($rangeEnd);
}
$newLength = $rangeEnd - $range + 1;
// Send Headers
header('HTTP/1.1 206 Partial Content');
header('Content-Length: ' . $newLength);
header('Content-Range: bytes ' . $range - $rangeEnd / $fileSize);
}
else {
$newLength = $fileSize;
header('Content-Length: ' . $fileSize);
}
// Output File
$chunkSize = 1 * (1024*1024);
$bytesSend = 0;
if($file = fopen($filePath, 'r')) {
if(isset($_SERVER['HTTP_RANGE'])) {
fseek($file, $range);
while(!feof($file) && !connection_aborted() && $bytesSend < $newLength) {
$buffer = fread($file, $chunkSize);
echo $buffer;
flush();
$bytesSend += strlen($buffer);
}
fclose($file);
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
正如 Alex 的链接所解释的,您可能缺少
Content-Type
顶部的Content-Disposition
标题。所以像这样:
As explained by Alex's link you're probably missing the header
Content-Disposition
on top ofContent-Type
.So something like this:
根据 RFC 2046(多用途互联网邮件扩展):
所以我会选择那个。
Acoording to RFC 2046 (Multipurpose Internet Mail Extensions):
So I'd go for that one.
您可以尝试这个强制下载脚本。 即使您不使用它,它也可能会为您指明正确的方向:
You can try this force-download script. Even if you don't use it, it'll probably point you in the right direction:
更少的代码和一些安全改进
less code and some security improvements