实现下载计数器的最佳方法是什么?

3

假设用户点击下载链接,但在弹出的“另存为”对话框中选择了取消,该如何检测这种情况?也就是说,如果用户选择了取消或者没有完整下载文件,服务器不应记录该文件已被下载。


最佳方法计算网站文件下载次数 - Steven A. Lowe
@Steven A. Low:不,它明显不同,您所指的问题的解决方案依赖于日志处理。这里的 OP 明确要求完整下载。 - Kent Fredric
@[Kent Fredric]:只有一个解决方案,就是在日志中使用grep命令;下载过程发生在客户端,无法确定是否成功。这与之前的问题完全相同。 - Steven A. Lowe
@[Kent Fredric]:我看到你有一种替代方案;如果它有效,你应该在另一个线程上发布它。 - Steven A. Lowe
另一个线程处理下载开始,而这个线程侧重于下载完成,请关注“完成”一词。该解决方案对于第一种情况来说是不好的,因为它速度较慢,而且更容易出现错误。 - Kent Fredric
另请参阅:https://dave.autonoma.ca/blog/2023/11/12/php-download-hit-counter/ - undefined
5个回答

3

过去我已经做过这个:

header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
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));
ob_clean();
flush();
readfile($file);
flush()
log()
exit;

日志应该在读取文件完成后运行,这意味着整个文件已经被流式传输给用户。我已经有一段时间没有做过这个了,也不知道确切的代码在哪里,所以可能不是很准确,但应该能让你开始。

编辑:我在php.net上找到了上述代码,它或多或少就是我描述的内容,这种方法应该有效。


我会在readfile()之后放置一个“flush”调用,以保险起见。 - Kent Fredric
另外,由于我最近给你点了赞,你的分数现在是“5432”,恭喜:D - Kent Fredric
@kent 你关于flush的想法可能是正确的,我打算稍后进行一些测试以确定,直到你说了5432我才注意到。哈哈谢谢。 - UnkwnTech
我还没有测试过它,但它很有道理,所以我已经添加了它。 - UnkwnTech
它不起作用;当保存对话框出现在用户屏幕上时,Web服务器不会阻塞。发生的情况是PHP将I/O请求发送到Web服务器,然后Web服务器将其移交给操作系统。它似乎只能工作是因为对于较大的文件,“取消”会在I/O完成之前终止PHP。 - Nathan Strong
显示剩余2条评论

2
我认为一些HTTPd在请求未完成时不会将文件记录到访问日志中。您可以尝试编写一个解析日志、搜索文件名并计算行数的程序。
编辑:我刚刚尝试了nginx,证实了我的理论。我相信Apache也是如此。
编辑#2:为什么这个回答被评为负分?根据SO上的重复链接,这实际上是正确的答案。我被证明是正确的。

我不确定为什么有些东西在这里被评为负分。我只是提交了一个问题,然后被某个人“关闭”,因为“这不是一个真正的问题”,但实际上它确实是一个问题。 - Sampson
我第二次阅读后改变了我的投票,你的句子结构让我第一次错误地解释了它。 - Kent Fredric

1

当服务器发送文件时,它并不会立即在网络上传输,需要在客户端和服务器之间发送确认包来一次一个块地发送文件。

只需要有某种处理方式以管理方式将文件发送给用户,以便可以在文件的“最后”块刷新到用户时挂钩事件。

现在假设,用户可能永远收不到这个最后的数据块,但是如果他们请求获取最后的数据块(这就是确认包所做的),则他们或底层网络协议已接受了接收到前面所有数据块的事实,并且可以继续发送最后的数据块。

现在假设:

  1. 您的Web服务器没有缓冲。
  2. 当您的Web服务器阻塞您的程序以中继您发送的信息时。

只需使用类似于以下的简单结构:(伪代码)。

1: Open File $foo; 
2: Loop while $foo is not EOF
     3: read  700kilobits from file $foo; 
     4: print 700kilobits of read data to web server
     5: execute webserver 'flush' which blocks until flush is complete. 
     6: <-- Loop
7: If all chunks of 700kilobits were read before user aborted transaction
     8: report file was downloaded. 

编辑 Unkwntech 的回答实际上与我的回答相同。
他的回答是针对 PHP 的,而我想提供一个更通用的解决方案,主要是因为我对 PHP 开发产生了厌恶,不愿意编写代码并设置 Web 服务器来测试它是否有效,只是为了证明我从经验中知道的东西。
对于这一点我表示歉意。


非常有趣 - 你测试过这个解决方案吗? - Steven A. Lowe
@kent 谢谢指出来,我本来想说的,但是又不想听起来像个傻瓜。 - UnkwnTech

0

你是在谈论任何文件吗?如果它是一个程序,你可以让它在安装时调用服务器吗?除此之外,我同意Jonathan的看法,我不确定你是否有权限访问那个。


0

дёҖз§Қж–№жі•жҳҜзј–еҶҷдёҖдәӣPHPд»Јз ҒжқҘеӨ„зҗҶдҪңдёә$_REQUESTеҸӮж•°жҸҗдҫӣзҡ„е®һйҷ…ж–Ү件传йҖ’гҖӮиҜҘи„ҡжң¬е°Ҷжү§иЎҢи®°еҪ•дёӢиҪҪзҡ„е®һйҷ…е·ҘдҪңгҖӮ


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