创建临时文件并自动删除

14
我正在编写一个反盗链下载脚本,我的计划是创建一个临时文件,文件名由会话ID命名,然后在会话过期后自动删除该文件。这可能吗?您能给我一些用PHP如何实现的技巧吗?
非常感谢任何回复。

文件应该保留多久? - Ikke
你能否更具体地说明你想要实现什么?Anti-Leeching DL Script 这个名称有点含糊不清。这个脚本的用例或者解决的问题是什么? - Gordon
嗨lkke, 我只想让用户在他/她的会话中下载,就像他不能简单地将链接复制并粘贴给其他人。嗨Gordon, 我想强制用户从我的网站下载文件,并控制下载速度。因此,我打算在HTTP文档的临时目录中创建一个临时文件,以便用户可以下载它们,然后在会话过期后自动删除它们。 - mrblue
也许你可以以某种方式使用用户的IP地址? - Philippe Gerber
抱歉,我想你是对的。请检查更新后的答案。 - Fluffy
7个回答

13

PHP有一个名为tmpfile的函数。它会创建一个临时文件并返回一个资源。这个资源可以像其他资源一样使用。

例如,手册中的示例:

<?php
$temp = tmpfile();
fwrite($temp, "writing to tempfile");
fseek($temp, 0);
echo fread($temp, 1024);
fclose($temp); // this removes the file
?>

文件会在关闭(使用fclose())时自动删除,或者在脚本结束时删除。您可以在资源上使用任何文件函数。 您可以在这里找到它们。希望这可以帮助您?

另一种解决方法是以常规方式创建文件,并使用cron作业定期检查会话是否过期。过期日期和其他会话数据可以存储在数据库中。 使用脚本查询该数据并确定会话是否已过期。如果是,则从磁盘物理上将其删除。确保每小时运行一次脚本(取决于超时时间)。


我认为这不是mrblue问题的解决方案,因为文件是在脚本结束/fclose()时删除的,而不是在会话过期时删除。 - Philippe Gerber
你说得对,我猜。可能是因为看漏了那部分。我在帖子中添加了更多信息。现在取决于他了 ;) - TheGrandWazoo
嗨TheGrandWazoo,感谢您的回答。我已经考虑过那个解决方案,但如果网站扩展规模,由于性能问题可能不可行。而且Philippe是正确的,我的担忧是PHP支持“钩子”函数,例如在会话过期或开始后自动调用。 - mrblue
使用定时任务时,无需涉及数据库。当dl文件与该用户的会话文件共享名称时,由cron调用的脚本只需删除当前不存在会话文件的所有dl文件即可。 - Gordon

10

所以,我们有一个或多个文件可供下载。为每个下载请求创建临时文件不是一个好主意。相反,为每个文件创建symlink()是一个更好的选择。这将节省大量磁盘空间并降低服务器负载。

将符号链接命名为用户会话名称是一个不错的主意。更好的主意是生成一个随机符号链接名称并与会话相关联,以便脚本可以处理单个会话中的多个下载。您可以使用session_set_save_handler()(链接)并注册一个自定义read函数来检查过期会话并在会话过期时删除符号链接。


1
嗨,pygorex1,这正是我正在寻找的。非常感谢。 - mrblue
链接现在是404,只是ping了你一下,以便你可以修复它。 - Mr. Alien
如果这是一个API请求,而不是带有会话的Web应用程序呢? - Fadi

3

好的,目前我们有以下需求:

  1. 让用户只能在他/她的会话中下载
  2. 禁止将链接复制并发送给其他人
  3. 用户必须从网站上下载,例如禁止热链接
  4. 控制下载速度

让我们看看。这不是可运行的代码,但它应该按照这些思路工作:

<?php // download.php

session_start(); // start or resume a session

// always sanitize user input
$fileId  = filter_input(INPUT_GET, 'fileId', FILTER_SANITIZE_NUMBER_INT);
$token   = filter_input(INPUT_GET, 'token', FILTER_UNSAFE_RAW);
$referer = filter_input(INPUT_SERVER, 'HTTP_REFERER', FILTER_SANITIZE_URL);
$script  = filter_input(INPUT_SERVER, 'SCRIPT_NAME', FILTER_SANITIZE_URL);

// mush session_id and fileId into an access token
$secret        = 'i can haz salt?';
$expectedToken = md5($secret . session_id() . $fileId);

// check if request came from download.php and has the valid access token
if(($expectedToken === $token) && ($referer === $script)) {
   $file = realpath('path/to/files/' . $fileId . '.zip');
   if(is_readable($file)) {
        session_destroy(); // optional
        header(/* stuff */);
        fpassthru($file);
        exit;
    }
}
// if no file was sent, send the page with the download link.
?>
<html ...

<?php printf('a href="/download.php?fileId=%s&amp;token=%s', 
              $fileId, $expectedToken); ?>

...
</html>

就是这样,不需要数据库。这应该满足1-3的要求。你不能用PHP控制速度,但如果在发送文件后不销毁会话,你可以将计数器写入会话中,并限制用户在一次会话期间发送的文件数量。

我完全同意,这种猴子表格黑客技巧可以更优雅地解决,但作为概念证明,它应该足够了。


嗨,戈登,这几乎是我在代码中编写的90%,但你的代码使用令牌进行了更好的安全检查。非常感谢。 - mrblue
令牌添加了安全性,但也使得您不再需要创建符号链接或复制文件,因为令牌对于session+file是唯一的。该令牌基本上是pygorex1为符号链接名称创建的内容。只需将名称/令牌与常规fileId一起发送,而不必创建符号链接并最终删除它,这样可以减少维护量。 - Gordon

3

您能详细解释一下您的问题吗?因为我不明白为什么不能使用$_SESSION。顺便提一下,$_SESSION中的数据是存储在服务器端的文件中的(请参见http://php.net/session.save-path)。至少默认情况下是这样的。;-)


嗨,菲利普,是的,我真的想使用 $_SESSION(实际上我已经使用了),但我找不到任何文件或主题来提及类似“钩子”操作之类的东西,例如:我们可以创建一个函数,系统在会话过期或开始后自动调用。这是我的想法和关注点。感谢您的时间。 - mrblue
走另一条路。当创建新会话时(例如,$_SESSION为空且您之前已填充它)才做出反应,而不是在会话过期时。问题是,会话可能会在用户未采取任何操作的情况下过期(会话已过期并由垃圾收集器删除)。您到底想做什么? - Philippe Gerber

2
我建议您不要在第一时间复制文件。我会采取以下措施:当用户请求文件时,您生成一个随机唯一字符串,以此方式提供链接:dl.php?k=hd8DcjCjdCkk123,然后将此字符串放入数据库中,存储他的IP地址、可能的会话和您生成链接的时间。然后另一个用户请求该文件,确保所有内容(哈希值、IP等)匹配且链接未过期(例如,自生成以来不到N小时),如果一切正常,则使用PHP管道传输文件。设置一个cron作业来查看数据库并删除过期条目。您认为呢? tmpfile 创建一个临时文件,文件名唯一,以读写(w+)模式打开并返回文件句柄。关闭文件(使用fclose())或脚本结束时,文件将自动删除。

我认为这不是mrblue问题的解决方案,因为文件是在脚本结束/fclose()时删除的,而不是在会话过期时删除。 - Philippe Gerber
嗨roddik,Philippe是对的,我考虑过那个解决方案,但在我的情况下不适用,尤其是性能问题。 - mrblue

0
为了实现这个目标,我会创建一个文件并使用chmod命令来保护它,使其对公众不可见。或者,将内容保存在数据库表行中,在需要时提取它。
将其作为文件可下载。为此,我会从受保护的文件中获取内容,或者如果它存储在数据库表中,则获取它并简单地输出它。使用php头文件,我会指定所需的名称、扩展名、类型,并最终强制浏览器将输出作为实体文件下载。
这样,您只需要在一个地方保存数据,无论是在受保护的文件中还是在数据库中。根据条件多次强制客户端浏览器下载,例如只要用户已登录等。而无需担心磁盘空间、创建任何临时文件、cronJobs或自动删除文件。

0
也许回答有点晚了,但我想分享一下 googlize 的功能!如果你使用 CPanel,在你的托管文件上阻止外部请求有一个简短快速的方法,那就是 HotLink。你可以在 CPanel 中启用 HotLinks,并确保没有人可以从另一个主机请求您的文件或将您的文件用作下载引用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接