使用头(“Location:..”)进行文件下载的任何缺点


Any downsides to using header("Location:...") for file downloads?

我正在我的网站上设置MP3下载,希望人们点击电子邮件中的链接下载MP3的ZIP文件。我希望他们点击的URL不要直接指向文件,这样我就不会透露实际位置——我也可能会给下载等添加时间限制。

所以我一直在使用readfile($file_url),并尝试了很多header()选项,但没有走多远。然后我读到你可以使用:

header("Location: " . $file_url);

这在我测试过的浏览器中似乎可以正常工作。与readfile($file_url)相比,这样做有什么缺点吗?

谢谢!

没有什么严重的问题,只是你实际暴露了文件的实际位置(攻击者可能会试图猜测他不应该访问的文件的地址),客户端将不得不向服务器发出额外的请求(下载延迟几毫秒),有时,Web浏览器会因为各种原因而决定不进行重新定位。

好的一面是,这种方式不需要通过PHP处理文件,节省了一点点服务器资源(与处理新的http请求相比,readfile()函数非常优化,这是非常值得怀疑的)


回到您最初的问题,当通过PHP提供下载文件时,您不需要超过以下示例中的3行内容:

<?php
//code copied from http://php.net/manual/en/function.header.php
// We'll be outputting a PDF
header('Content-type: application/pdf');
// It will be called downloaded.pdf
header('Content-Disposition: attachment; filename="downloaded.pdf"');
// The PDF source is in files/originalfile.pdf
readfile('files/originalfile.pdf');
?>

在使用Header()函数<-之前,只需确保不打印任何字符串(甚至不打印源代码中的UTF8 BOM字符)常见问题。

"不利因素"取决于您试图实现的目标。

通过设置Location标头,您正在发送重定向。实际上,浏览器将收到一个响应,其中包含HTTP状态代码302("已找到")和资源的新URL。基本上,您正在将它们重定向到另一个资源
为了实现这一点,您将它们重定向到的资源必须是可公开访问的。也就是说:它需要是服务器"文档根"中的现有文档。

这种方法具有某些特性,根据您的要求,可能被视为缺点:

  1. 如上所述,资源必须是可公开访问的。因此,您不能将MP3文件放在单独的、不可访问的文件夹中。事实上,假设您的文档根在/var/www/public中,您可能希望将这些文件放在一个不可访问的文件夹中,如/var/www/resources,并仅通过PHP脚本提供这些文件
  2. 您的用户将收到资源的URL,因此他们可以直接访问资源,而无需通过您的PHP脚本

如果这些"特性"对您不利,那么您可能需要避免使用Location标头。

一些提供MP3文件的PHP代码可以是:

$file = 'local/path/to/your_audio_file.mp3'; // the path on the local file system - ie. don't use "http://www.example.com"!
header('Content-Description: File Transfer');
header('Content-Type: audio/mpeg'); // audio/mpeg is the correct mime type for mp3 files
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
exit; // Could be a good idea to exit at the end

有些标题可能是多余的,但它们是为了确保最佳的跨浏览器兼容性。我从http://php.net/manual/en/function.readfile.php