There are a lot of examples on implementation. Interested in a more detailed part, namely. How do I know that the file has already downloaded?

Situation: The script generates files on the server in a temporary folder, then the upload script is used (http_range, 206 partial content, etc.). How do I know when to delete it?

Offers with krone not to offer!

  • I correctly understood that you want to know when the file is downloaded completely at least once? - Ilya Pirogov
  • Yes that's right. You can give an intermediate version, as soon as I myself will go to this: I need a link to the documentation describing HTTP_RANGE. And then the Internet is cluttered with any nonsense, like the answer below (function file_download ($ filename, $ mimetype = 'audio / mpeg'). It’s written that there are a lot of examples. Only the answer to the question is interesting. For example: how to define this function that file has already skipped! - org
  • By the way, I’ll add that you need to consider the presence of nginx. If you simply count how many bytes are downloaded, then nginx will be the client for the script, but did it get to the program (falshget for example) from nginx? Therefore, we need documentation. Maybe there is a parameter in the protocol that answers that all-or the last portion or all-I downloaded ... - org


2 answers 2

Determine in PHP that the file was guaranteed to download, unfortunately, will not succeed. However, if nginx is used as a frontend, then you can at least make sure that the file (in this case, part of the file) has been given.

In nginx there is a directive post_action , which makes a repeated internal request after the answer has already been sent to the client. Accordingly, thanks to it, you can find out how much real data was sent to the client. The config will look like this:

 # скрипт, который отдает файл по частям location = /download.php { fastcgi_pass localhost:9000; fastcgi_param SCRIPT_FILENAME /var/www/localhost/htdocs/$fastcgi_script_name; include fastcgi_params; post_action /complete; } # а этот скрипт уже будет подсчитывать байты location = /complete { internal; fastcgi_pass localhost:9000; fastcgi_param SCRIPT_FILENAME /var/www/localhost/htdocs/complete.php; # количество отправленных клиенту байт fastcgi_param BYTE-SEND $body_bytes_sent; # заголовок Range fastcgi_param RANGE $http_range; include fastcgi_params; } 

After download.php will send another piece of the file, nginx will make an internal request to complete.php, where in $_SERVER['BYTE-SEND'] will be the number of bytes sent, and in $_SERVER['BYTE-SEND'] Range header from the client.

Well, after that is a matter of technology. We consider somewhere (for example in a session) bytes or, for greater reliability, Range spans are better. And the code, the number of bytes given will be equal to the file size - it means the file was given entirely.

PS Unfortunately the post_action directive is not documented.

UPD. If PHP works through fastcgi or any reverse-proxy stands behind it (again, nginx for example), then it is useless to define it on PHP itself. After PHP gives the web server a piece of data, the further fate of it is no longer known to him.

If PHP works on apache via mod_php without any reverse-proxy, then it is probably a stretch to assume that after flush() data has gone to the client.

Another way is to give the file directly through the sockets. Those. write a script that would itself act as a web server. However, this is not a very productive solution.

All other options for PHP, I do not see.

  • Thanks for the professional answer. Ilya, I marked your answer correct, but let's not close it. Your answer can be used in case of access to nginx. Let's think about the option where we rent a place from the hoster: maybe there is nginx there, but it does not allow to change its config or it is not there. Is it possible to "dig" in this direction: the connection with the client is via tsp, hence there are probably commands to the http server from the php side about whether the user session is interrupted. My terminology is lame, but I think the idea is clear. Your opinion is important to me. What do you think about that? - org
  • Ideally, I would like to use only php. Because it is an integral part of the distributed software .... It is only known that php will work there and the necessary libraries can be connected to it ... - org
  • Updated the answer. - Ilya Pirogov

Unfortunately, I have been writing such a script for a long time, and I don’t really want to remember it. Here he himself, I hope, will understand.

 <?php set_time_limit(0); function file_download($filename, $mimetype='audio/mpeg') { $download_speed = 51200; // 51200 $time_discret = 1; if (file_exists($filename)) { $f = fopen($filename, 'r'); if (isset($_SERVER['HTTP_RANGE'])) { $load_from = preg_replace('#[^0-9]#', '', $_SERVER['HTTP_RANGE']); fseek($f, $load_from); $filesize = filesize($filename); header('HTTP/1.1 206 Partial Content'); header('Content-Type: ' . $mimetype); header('Content-Range: bytes '.$load_from.'-'.$filesize.'/'.$filesize); header('Last-Modified: ' . gmdate('r', filemtime($filename))); header('ETag: ' . sprintf('%x-%x-%x', fileinode($filename), filesize($filename), filemtime($filename))); header('Accept-Ranges: bytes'); header('Content-Length: ' . (filesize($filename))); header('Connection: close'); header('Content-Disposition: attachment; filename="' . basename($filename) . '";'); } else { header($_SERVER["SERVER_PROTOCOL"] . ' 200 OK'); header('Content-Type: ' . $mimetype); header('Last-Modified: ' . gmdate('r', filemtime($filename))); header('ETag: ' . sprintf('%x-%x-%x', fileinode($filename), filesize($filename), filemtime($filename))); header('Content-Length: ' . (filesize($filename))); header('Connection: close'); header('Content-Disposition: attachment; filename="' . basename($filename) . '";'); } if((int) $download_speed > 0) { while(!feof($f)) { $time_start = microtime(true); echo fread($f, ceil($download_speed*$time_discret)); flush(); $time_end = microtime(true); $time = $time_end - $time_start; if($time_discret-$time > 0) usleep(($time_discret-$time)*1000000); } } else { while(!feof($f)) { echo fread($f, 1024); flush(); } } fclose($f); } else { header($_SERVER["SERVER_PROTOCOL"] . ' 404 Not Found'); header('Status: 404 Not Found'); } exit; } # file_download('./20_dt8_project.mp3', $mimetype='audio/mpeg'); ?> 

The script sends the file to the client for download, and if you need to download a file, it downloads the file. I took parts from sites, I wrote a part myself, there is a resume, one of the headers says that it exists. But I immediately warn you, such a script loads the server very heavily.

  • An example is stupidly copied from an Internet. This is not the answer to my question. And here's a tip: To prevent this script from loading the server, you need to finish it! Why it loads: it loads because you throw the file from the range to the very end, but you need the portion you requested. On the fingers it looks like this: file 5 gigs, I received a request, give me a part of the file starting with 1024 characters. What are you doing-give from 1024 characters to the end of the file. And how many such pieces, if you ask for 1024: or rather, how many times will there be fread called there in a loop? - org
  • Nobody will do anything for you. - Andrey Arshinov
  • "No one will do anything for you." - and here is the answer? I'm here for advice, or a hint at the algorithm, but not for the implementation! And your answer has already been posted many times in the internet, which I wrote about in a post. I do not need the function of supporting the resume from the server side to PCP, but the ability to determine whether the file has been downloaded or not by this method to the end. - org