How to Detect When a User Has Successfully Finished Downloading a File in PHP

How to detect when a user has successfully finished downloading a file in php

Handle the download in a seperate php script (better do a little more than just readfile($file);, you can also provide the ability to resume downloads like in this question).
Then in this script, when you read the last block and send it, you know that all the file was sent. This is not the same as knowing that all was received, but it should be enough for most scenarios.

Detecting whether a user downloaded a file

There is no way to determine whether a user has downloaded the file or not.

In your case, just don't save the file. Generate it into a string and send this one along with appropriate headers.

how to detect if the downloading of the file has finished?

This type of long running processes are not well suited for the typical web request / response workflow. Yes, it will work...but I believe there are issues and the user experience is not ideal. I better workflow would be to do one of the following:

  1. Cache the report. If the data in the report can be stale for a
    period (e.g. 1 day) then setup a cron to generate the report and
    then return the cached report to the user. The user experience will
    be an immediate download.

  2. If the report needs to be generated on the fly, then have the user request the report and return a result immediately. Later deliver the report via a notification system (assumes they have a dashboard or something on your site) or email (the must provide you an email address when requesting the report).

I think that these two options provide a better user experience.

check if download is completed

That's not really going to tell you if the download has completed for the user. It will tell you when you have finished sending bytes to the user, but it says nothing about how many of them the user has actually received.

In reality, there is no way with PHP (which is a server-side, not a client-side, language) to truly detect when a file download has completed. The best you can do is log the download in your database when it begins. If you absolutely, completely and totally need to know when the download has completed, you'll have to do something like embed a Java applet or use Flash. However, usually that is not the correct answer in terms of usability for your user (why require them to have Java or Flash installed just to download something from you?).

Trigger action when file download actually completes

This can be done by using my other answer as base How can I give download access to files outside public_html directory? and replacing readfile( $filename )

with readfileWhileConnected( $filename ):

Read file until EOF or disconnect:

/** Read $filename until EOF or disconnect, 
* if disconnect then error_log() count of bytes read already
*/
function readfileWhileConnected( $filename ) {
// Save and set ini values:
$user_abort = ignore_user_abort();
ignore_user_abort(false);
// Get file size and set bytes_sent to zero:
$fsize = filesize($filename);
$bytes_sent = 0;
// Open file:
$f = fopen($filename, 'r');
// Read file:
while($chunk = fread($f, 1024)) {
// Check if connection is still open:
if(!connection_aborted()) {
// Send $chunk to buffer (if any), then flush() buffers:
echo $chunk;
flush();
// Add $chunk length to $bytes_sent
$bytes_sent += strlen($chunk);
} else {
// Close file:
fclose($f);
error_log("Connection closed at $bytes_sent/$fsize");
exit();
}
// Close file:
fclose($f);
// Reset ini values:
ignore_user_abort($user_abort);
return $bytes_sent;
}
}

After you have your new shiny class myNewSuperDownloadHandlerClass { ... } ready, then make sure you only serve downloads through filedownload.php described here or if have done good myNewSuperDownloadHandlerClass(), then use that, just make sure that readfileWhileConnected() is used for every download requiring connection status polling.

You can easily add callback to be triggered if user closes connection, only 2 exit points here. (seen many functions that have every often return false; return true; return null; return false; return true; and so on..)



Related Topics



Leave a reply



Submit