tags:

views:

1847

answers:

6

Hi

I have a php script on a server to send files to recipents: they get a unique link and then they can download large files. Sometimes there is a problem with the transfer and the file is corrupted or never finishes. I am wondering if there is a better way to send large files

Code:

$f = fopen(DOWNLOAD_DIR.$database[$_REQUEST['fid']]['filePath'], 'r');
while(!feof($f)){
    print fgets($f, 1024);
}
fclose($f);

I have seen functions such as

http_send_file
http_send_data

But I am not sure if they will work.

What is the best way to solve this problem?

Regards
erwing

+1  A: 

For downloading files the easiest way I can think of would be to put the file in a temporary location and give them a unique URL that they can download via regular HTTP.

As part generating these links you could also remove files that were more than X hours old.

Andrew Grant
I don't like this answer because it requires additional use of crons or such to remove the old files, and that adds another layer of complexity and another point of failure to the system, but I'm not going to vote it down because it is a valid answer.
Unkwntech
@Unkwntech - no need for cron, as mentioned while generating new files you can also discard older ones. Lots of websites perform cron-like tasks as part of another call.
Andrew Grant
@Andrew Grant, you are correct that it could be done without a CRON, but I still feel that it adds an extra point of failure, whereas with my answer I don't think that it is adding an extra failure point.
Unkwntech
But what you get out of it is punting the complexity of the actual HTTP communication to the server, where it belongs (things like expire headers, range downloads, caching, etc.). *I* would rather clean up a few files with a script than reimplement HTTP in my application.
Will Hartung
+5  A: 

When I have done this in the past I've used this:

set_time_limit(0); //Set the execution time to infinite.
header('Content-Type: application/exe'); //This was for a LARGE exe (680MB) so the content type was application/exe
readfile($fileName); //readfile will stream the file.

These 3 lines of code will do all the work of the download readfile() will stream the entire file specified to the client, and be sure to set an infinite time limit else you may be running out of time before the file is finished streaming.

Unkwntech
Of course, if this isn't an exe, the mime type should be different.
configurator
Also, never-ever-ever combine set_time_limit(0) with ignore_user_abort() or the script could be running forever.
Pim Jager
Very true, unless of course you want the script to continue running after the user dies.
Unkwntech
That would leave evidence that you killed the user. Not exactly a good idea.
envalid
+4  A: 

If you are sending truly large files and worried about the impact this will have, you could use the x-sendfile header.

From the SOQ using-xsendfile-with-apache-php, an howto blog.adaniels.nl : how-i-php-x-sendfile/

garrow
Thanks, I didn't know about that one.
lpfavreau
A: 

If you are using lighttpd as a webserver, an alternative for secure downloads would be to use ModSecDownload. It needs server configuration but you'll let the webserver handle the download itself instead of the PHP script.

Generating the download URL would look like that (taken from the documentation) and it could of course be only generated for authorized users:

<?php

  $secret = "verysecret";
  $uri_prefix = "/dl/";

  # filename
  # please note file name starts with "/" 
  $f = "/secret-file.txt";

  # current timestamp
  $t = time();

  $t_hex = sprintf("%08x", $t);
  $m = md5($secret.$f.$t_hex);

  # generate link
  printf('<a href="%s%s/%s%s">%s</a>',
         $uri_prefix, $m, $t_hex, $f, $f);
?>

Of course, depending on the size of the files, using readfile() such as proposed by Unkwntech is excellent. And using xsendfile as proposed by garrow is another good idea also supported by Apache.

lpfavreau
A: 

Best solution would be to rely on lighty or apache, but if in PHP, I would use PEAR's HTTP_Download (no need to reinvent the wheel etc.), has some nice features, like:

  • Basic throttling mechanism
  • Ranges (partial downloads and resuming)

See intro/usage docs.

A: 

I'm not sure this is a good idea for large files. If the thread for your download script runs until the user has finished the download, and you're running something like Apache, just 50 or more concurrent downloads could crash your server, because Apache isn't designed to run large numbers of long-running threads at the same time. Of course I might be wrong, if the apache thread somehow terminates and the download sits in a buffer somewhere whilst the download progresses.