views:

906

answers:

3

My site is Downloading one file using this code:

$flv = **filename**

$ch = curl_init($flv);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); //not necessary unless the file redirects (like the PHP example we're using here)
$data = curl_exec($ch);

curl_close($ch);
if ($data === false) {
  echo 'cURL failed';
  exit;
}

$contentLength = 'unknown';
if (preg_match('/Content-Length: (\d+)/', $data, $matches)) {
  $contentLength = (int)$matches[1];
}

header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($flv));
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: ' . $contentLength );
ob_clean();
flush();
readfile($flv);

The code works but, until you finish the download the links one the page are bloqued, waiting for the download to finish!

What can I do?

Regards,
Pedro

+1  A: 

How are you linking to this page. Of course a download will block everything else on the page since it will pop up a modal window allowing the user to choose where to download the file or cancel download. This is a browser feature.

If you are talking about something else, please explain.


Edit:

Which download are you referring to? There are two downloads here. One is the server to server download of the file using CURL. The other is the download of that same file by the HTTP client from your server. Both can be "fixed" but you have to be specific.

If you want the CURL download to be automatic, then you have to do that download is a separate process. Unfortunately PHP only supports a single process normally, so you have to use non-standard PHP.

Possibilities:

1) Use the Shell

exec('php filename.php downloadfile.ext &');

Here you send the PHP script filename.php an argument download.ext. The whole process will be executed in the background due to the & at the end of the command. Do a var_dump($argv); to see the arguments passed.

You can also use process pipe to do the same:

eg: poopen() proc_open()

They give you a bit more control.

2) You can also install one of the process control extensions. http://php.net/manual/en/refs.fileprocess.process.php

This would allow you to fork a separate process from your PHP script which woudl handle the download of the file to your server. There are also inter process communication (IPC) extensions that will allow you to keep track of whats happening in the separate process.

3) You could write a deamon to handle downloads. Basically, you'll also need to handle multiple processes in your deamon in order to make multiple downloads at once. You'll also need a bit of IPC or at least some form of concurrency so you don't download the same resource more then once. Thats getting a bit complex so I think 1 and 2 are better options.


Basically what you're trying to do is stop the user from waiting for the server to server download here.

So you start out with the main PHP process (the normal PHP page). Then you fork the process somehow. Have that child process download the file using CURL. In the meantime, the parent PHP process returns immediately to the HTTP client. When the child has finished donwloading, the parent process can either know via IPC, or by polling a concurrent resource, such as the filename (or filesize, or lock) of the downloaded file. It then serves this to the HTTP client.


If your problem is with the HTTP Client, then you'll have to be looking at a browser solution. Given the exact browser you're testing with, etc. etc. would give an insight on the problem.

bucabay
+1  A: 

try with:

$ContentLength = filesize($flv);

header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($flv));
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: ' . $ContentLength);

$fh = fopen($flv, 'r');
echo fread($fh, $ContentLength);
fclose($fh);
andres descalzo
This will download the file twice, which is a waste of time.
Andrew Moore
Well I found one solution if we put session_write_close() before the download I will not wait for the download to finish, so Problem Solved.Thanks GUYS!
Pedro
Download the file twice?, are you check with some snifer?
andres descalzo
A: 

The .flv is on a remote server, right?

So you use cURL to download the whole file into $data. This will take some time. Then you output some headers, etc. and then read the whole remote file again with readfile()?

Edit

Missed the curl_setopt($ch, CURLOPT_NOBODY, true);, sorry!

Philippe Gerber