3

Say a user clicks on a link to download a file. The user gets the save as dialogue box but then clicks on cancel. How do you detect this? Meaning if the user hits the cancel link or doesn't get the whole file, the server should not record that the file was downloaded.

jdelator
  • 4,101
  • 6
  • 39
  • 53
  • duplicate of http://stackoverflow.com/questions/158124/best-way-to-count-file-downloads-on-a-website – Steven A. Lowe Jan 14 '09 at 02:23
  • @ Steven A. Low : No, its distinctly different, the solutions on the question you refer to rely on LOG processing. He OP here distinctly asked for *COMPLETE* downloads. – Kent Fredric Jan 14 '09 at 02:54
  • @[Kent Fredric]: there is only one solution, grep the log; the download process happens on the client side and there is no way to tell if it was successful. This is exactly the same question. – Steven A. Lowe Jan 14 '09 at 03:23
  • @[Kent Fredric]: I see that you have an alternative solution; if it works you should post it on the other thread. – Steven A. Lowe Jan 14 '09 at 03:27
  • The other thread deals with download *starts* this one is download *completion* centric, focus on the word *completion*. This solution is *bad* for the first case, as its *slower* and more prone to bugs. – Kent Fredric Jan 14 '09 at 03:33

5 Answers5

3

In the past I have done this:

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;

log should only run after readfile is complete meaning the entire file has been streamed to the user. It's been a while since I have done this and I don't know where the exact code is so this may not be exact but it should get you going.

EDIT I found the above code on php.net and it is more or less what I was describing, this method should work.

UnkwnTech
  • 88,102
  • 65
  • 184
  • 229
  • This should work for any file, regardless of size, readfile streams the file to the user log cannot be run until readfile has completed. – UnkwnTech Jan 14 '09 at 23:41
  • I would put a "flush" call after readfile(); just to be safe. – Kent Fredric Jan 15 '09 at 05:40
  • Also, with my recent upvote, your score reads "5432", congrats :D – Kent Fredric Jan 15 '09 at 05:46
  • @kent you'r probably correct about the flush, I intend to do some testing a bit later so see for sure, and I didn't even notice the 5432 until you said it. LOL THX. – UnkwnTech Jan 15 '09 at 05:57
  • I was going to post what Kent said. A final flush ensures the file was "sent". Of course even this doesn't stop the user from hitting cancel. – jmucchiello Jan 15 '09 at 09:27
  • I havn't had the opertunity to test it, but it makes sense so I have added it. – UnkwnTech Jan 15 '09 at 22:49
  • It does not work; the web server does not block while the save as dialog is on the user's screen. What happens is that PHP sends the I/O request to the web server, which in turn hands it off the the O/S. It only seems to work because with larger files, "cancel" terminates PHP before the I/O is done. – Nathan Strong Jan 16 '09 at 06:50
2

I think some HTTPd's don't log files into the access log until the request is rendered complete. You could try writing something that parses the log, greps the filename, and counts the number of lines.

Edit: Just tried with nginx. Confirmed my theory. I believe the same is true for Apache.

Edit #2: Why was this modded down? It's actually the correct answer according to the dupe-link on SO. I stand vindicated.

Andrew
  • 1,216
  • 10
  • 17
  • I'm not sure why some things here are modded down. I just had a question I submitted "closed" by somebody because "it wasn't a real question," when in fact it was. – Sampson Jan 14 '09 at 17:29
  • I changed my vote after reading it a second time, The structure of your sentence lead me to mis-interpret it the first time for some reason. – Kent Fredric Jan 15 '09 at 05:41
1

When a server sends a file, it doesn't just stream over the wire instantly, there are ack packets that have to go between client and server sending the file out one chunk at a time.

All one has to do is have some sort of process that sends the file to the user in a managed way, so that you can hook an event on wherever the "last" block of the file is flushed to the user.

Now granted, the user might never recieve this last block, but if they ask for the last block ( which is what ack packets are doing ) then either they, or the underlying network protocol has accepted that they've received all the blocks preceding that last block, and that one may proceed to send the last block.

Now assuming that

  1. Your webserver does no buffering.
  2. That when your webserver blocks your program from executing while it relays the information you are sending.

All one needs is a simple construct like this: ( Pseudo Code ).

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. 

Edit Unkwntech's answer is effectively the same as my own.
His answer is however PHP specific and I wanted to provide a more language generic solution, primarily, because I've developed a distaste for PHP, and am loath to need to write code for it and set up a web-server and test it works just to prove a point that I know from experience works.
Apologies for being too lazy in this regard.

Community
  • 1
  • 1
Kent Fredric
  • 56,416
  • 14
  • 107
  • 150
0

Are you talking about any file? If it's a program you can have it make a call to the server on install? Other than that I agree with Jonathan, not really sure you have access to that.

Sean
  • 2,453
  • 6
  • 41
  • 53
0

one way would be to write some PHP that handles actually delivering the file in question supplied as a $_REQUEST parameter. That script would do the actual work of logging the download

Scott Evernden
  • 39,136
  • 15
  • 78
  • 84