Php 实现下载计数器的最佳方法?

Php 实现下载计数器的最佳方法?,php,Php,假设用户单击链接下载文件。用户获得“另存为”对话框,然后单击“取消”。你是如何发现的?这意味着,如果用户点击“取消”链接或未获取整个文件,服务器不应记录该文件已下载。您是否在谈论任何文件?如果它是一个程序,你可以让它在安装时调用服务器?除此之外,我同意Jonathan的观点,我不确定您是否有权访问该文件。我认为某些HTTPd在请求完成之前不会将文件记录到访问日志中。您可以尝试编写一些东西来解析日志、greps文件名和计算行数 编辑:刚刚尝试使用nginx。证实了我的理论。我相信Apache也是如

假设用户单击链接下载文件。用户获得“另存为”对话框,然后单击“取消”。你是如何发现的?这意味着,如果用户点击“取消”链接或未获取整个文件,服务器不应记录该文件已下载。

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

我认为某些HTTPd在请求完成之前不会将文件记录到访问日志中。您可以尝试编写一些东西来解析日志、greps文件名和计算行数

编辑:刚刚尝试使用nginx。证实了我的理论。我相信Apache也是如此


编辑2:为什么要修改?根据SO上的dupe链接,这实际上是正确的答案。我的观点是正确的。

一种方法是编写一些PHP来处理作为$\u请求参数提供的有问题的文件的实际交付。该脚本将完成记录下载的实际工作

当服务器发送文件时,它不仅会立即通过网络传输,还必须在客户端和服务器之间传递ack数据包,每次发送一个文件块

我们所要做的就是使用某种进程以托管方式将文件发送给用户,这样您就可以在将文件的“最后”块刷新到用户的任何位置上挂接事件

现在允许,用户可能永远不会收到最后一个块,但是如果他们请求最后一个块(这是ack数据包正在做的),那么他们或者底层网络协议已经接受他们已经收到最后一个块之前的所有块,并且可以继续发送最后一个块

现在假设

  • 您的Web服务器没有缓冲
  • 当您的Web服务器在转发您发送的信息时阻止您的程序执行时
  • 我们只需要这样一个简单的构造:(伪代码)

    1:打开文件$foo;
    2:循环,而$foo不是EOF
    3:从文件$foo读取700KB;
    4:将700KB的读取数据打印到web服务器
    5:执行Web服务器“flush”,直到flush完成为止。
    
    6:过去我做过这样的事情:

    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;
    
    日志应仅在readfile完成后运行,这意味着整个文件已流式传输给用户。我已经有一段时间没有这样做了,我不知道确切的代码在哪里,所以这可能不准确,但它应该让你去


    编辑我在php.net上找到了上面的代码,它或多或少就是我所描述的,这个方法应该可以工作。

    重复@Steven A。Low:不,它明显不同,你提到的问题的解决方案依赖于日志处理。他在这里明确要求完整下载。@[Kent Fredric]:只有一个解决方案,grep the log;下载过程发生在客户端,无法判断是否成功。这完全是同一个问题。@[肯特·弗雷德里克]:我看到你有另一个解决办法;如果它起作用,你应该把它发布到另一个线程上。另一个线程处理下载开始。这一个是以下载完成为中心的,关注单词完成。这个解决方案对于第一种情况来说是不好的,因为它速度较慢,更容易出现错误。非常有趣-你测试过这个解决方案吗?@kent谢谢你的提示,我要指出这一点,但我不想听起来像个傻瓜。我不确定为什么这里有些东西被修改了。我刚刚有一个问题被别人“关闭”了,因为“这不是一个真正的问题”,事实上是。我在第二次阅读后改变了投票,你的句子结构导致我第一次出于某种原因错误地解释了它。这应该适用于任何文件,无论大小,readfile将文件流式传输到用户日志直到readfile完成后才能运行。我会在readfile()之后进行“刷新”调用;为了安全起见。另外,根据我最近的投票结果,你的分数为“5432”,祝贺你:D@kent你可能是对的冲洗,我打算做一些测试后一点,所以看到肯定,我甚至没有注意到5432,直到你说它。哈哈,我正要把肯特说的话发出去。最终刷新确保文件已“发送”。当然,即使这样也不能阻止用户点击cancel。
    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;