Laravel 具有热链接保护和低内存使用率的下载响应
Laravel download response with hotlink protection and low memory usage
我有一个文件下载网站,我通过 Laravel 提供文件以防止盗链,但下载似乎使我的 php 进程保持运行很长时间(因为一些用户已经下载速度不佳)。
为了防止盗链,我在用户进入下载页面时创建一个会话,并在他们单击下载按钮时检查它。
有什么办法可以防止盗链或者我可以降低内存使用率吗?
这是触发下载的代码:
if($request->session()->get('file') == $apk->generated_filename)
{
$headers = array
(
'Content-Type' => 'application/vnd.android.package-archive'
);
Apk::find($apk->id)->increment('downloads_co');
return response()->download(config('custom.storage') . $apk->generated_filename, $apk->filename, $headers);
}
您可以为此使用 .htaccess。
您可以使用此 generator 或使用以下代码并自行调整。
RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?yourdomain.com [NC]
RewriteRule \.(jpg|jpeg|png|gif)$ - [NC,F,L]
location ~* \.(gif|png|jpe?g)$ {
expires 7d;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
# prevent hotlink
valid_referers none blocked ~.google. ~.bing. ~.yahoo. server_names ~($host);
if ($invalid_referer) {
rewrite (.*) /static/images/hotlink-denied.jpg redirect;
# drop the 'redirect' flag for redirect without URL change (internal rewrite)
}
}
# stop hotlink loop
location = /static/images/hotlink-denied.jpg { }
使用 X-Accel-Redirect
和 internal
location
绝对最好的方法是在nginx端使用http://nginx.org/r/internal,并在nginx的上游端使用X-Accel-Redirect
的HTTP响应Header字段进行响应处理。
除非 http://nginx.org/r/proxy_ignore_headers 等人阻止,否则 nginx 会对 X-Accel-Redirect
上游 HTTP 响应执行特殊处理 header — 它会导致 nginx 内部重定向(您应该对location
标有 internal
指令,以确保直接访问此类文件的唯一可能方法是通过此类内部重定向。
这里的想法是,您的 PHP 脚本仍然可以以您认为必要的任何方式处理身份验证和热 link 保护 — 用户身份验证、link 到期、个人 AI-based 黑名单和所有 — 但最终,一旦脚本完成,将文件实际提供给客户端将以最有效的方式直接通过 nginx 完成。
(请注意,使用 internal
关键字非常重要 - 它确保恢复下载的唯一方法,一旦因任何原因中断,首先联系您的 PHP 脚本。因此,借助 nginx 食谱中的这一巧妙且经过验证的技巧,您将获得两全其美的优势 — 完全控制热 linking 和最佳资源利用。)
您应该按缓冲区大小(例如 2k)读取文件,然后发送响应,不要一次发送整个响应,编写如下脚本来下载文件:
ignore_user_abort(true);
set_time_limit(0); \
$path = "/absolute_path_to_your_files/"; // change the path to fit your websites document structure
$dl_file = preg_replace("([^\w\s\d\-_~,;:\[\]\(\).]|[\.]{2,})", '', $_GET['download_file']); // simple file name validation
$dl_file = filter_var($dl_file, FILTER_SANITIZE_URL); // Remove (more) invalid characters
$fullPath = $path.$dl_file;
if ($fd = fopen ($fullPath, "r")) {
$fsize = filesize($fullPath);
$path_parts = pathinfo($fullPath);
$ext = strtolower($path_parts["extension"]);
switch ($ext) {
case "pdf":
header("Content-type: application/pdf");
header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\""); // use 'attachment' to force a file download
break;
// add more headers for other content types here
default;
header("Content-type: application/octet-stream");
header("Content-Disposition: filename=\"".$path_parts["basename"]."\"");
break;
}
header("Content-length: $fsize");
header("Cache-control: private"); //use this to open files directly
while(!feof($fd)) {
$buffer = fread($fd, 2048);
echo $buffer;
}
}
fclose ($fd);
我有一个文件下载网站,我通过 Laravel 提供文件以防止盗链,但下载似乎使我的 php 进程保持运行很长时间(因为一些用户已经下载速度不佳)。
为了防止盗链,我在用户进入下载页面时创建一个会话,并在他们单击下载按钮时检查它。
有什么办法可以防止盗链或者我可以降低内存使用率吗?
这是触发下载的代码:
if($request->session()->get('file') == $apk->generated_filename)
{
$headers = array
(
'Content-Type' => 'application/vnd.android.package-archive'
);
Apk::find($apk->id)->increment('downloads_co');
return response()->download(config('custom.storage') . $apk->generated_filename, $apk->filename, $headers);
}
您可以为此使用 .htaccess。
您可以使用此 generator 或使用以下代码并自行调整。
RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?yourdomain.com [NC]
RewriteRule \.(jpg|jpeg|png|gif)$ - [NC,F,L]
location ~* \.(gif|png|jpe?g)$ {
expires 7d;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
# prevent hotlink
valid_referers none blocked ~.google. ~.bing. ~.yahoo. server_names ~($host);
if ($invalid_referer) {
rewrite (.*) /static/images/hotlink-denied.jpg redirect;
# drop the 'redirect' flag for redirect without URL change (internal rewrite)
}
}
# stop hotlink loop
location = /static/images/hotlink-denied.jpg { }
使用 X-Accel-Redirect
和 internal
location
绝对最好的方法是在nginx端使用http://nginx.org/r/internal,并在nginx的上游端使用X-Accel-Redirect
的HTTP响应Header字段进行响应处理。
除非 http://nginx.org/r/proxy_ignore_headers 等人阻止,否则 nginx 会对 X-Accel-Redirect
上游 HTTP 响应执行特殊处理 header — 它会导致 nginx 内部重定向(您应该对location
标有 internal
指令,以确保直接访问此类文件的唯一可能方法是通过此类内部重定向。
这里的想法是,您的 PHP 脚本仍然可以以您认为必要的任何方式处理身份验证和热 link 保护 — 用户身份验证、link 到期、个人 AI-based 黑名单和所有 — 但最终,一旦脚本完成,将文件实际提供给客户端将以最有效的方式直接通过 nginx 完成。
(请注意,使用 internal
关键字非常重要 - 它确保恢复下载的唯一方法,一旦因任何原因中断,首先联系您的 PHP 脚本。因此,借助 nginx 食谱中的这一巧妙且经过验证的技巧,您将获得两全其美的优势 — 完全控制热 linking 和最佳资源利用。)
您应该按缓冲区大小(例如 2k)读取文件,然后发送响应,不要一次发送整个响应,编写如下脚本来下载文件:
ignore_user_abort(true);
set_time_limit(0); \
$path = "/absolute_path_to_your_files/"; // change the path to fit your websites document structure
$dl_file = preg_replace("([^\w\s\d\-_~,;:\[\]\(\).]|[\.]{2,})", '', $_GET['download_file']); // simple file name validation
$dl_file = filter_var($dl_file, FILTER_SANITIZE_URL); // Remove (more) invalid characters
$fullPath = $path.$dl_file;
if ($fd = fopen ($fullPath, "r")) {
$fsize = filesize($fullPath);
$path_parts = pathinfo($fullPath);
$ext = strtolower($path_parts["extension"]);
switch ($ext) {
case "pdf":
header("Content-type: application/pdf");
header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\""); // use 'attachment' to force a file download
break;
// add more headers for other content types here
default;
header("Content-type: application/octet-stream");
header("Content-Disposition: filename=\"".$path_parts["basename"]."\"");
break;
}
header("Content-length: $fsize");
header("Cache-control: private"); //use this to open files directly
while(!feof($fd)) {
$buffer = fread($fd, 2048);
echo $buffer;
}
}
fclose ($fd);