How can I allow a download to pause/resume? - php

Normally, when I want to allow a user to download a file without revealing the exact location, I just use something like this to let them download the file:
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"" . $filename) . "\";");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($filename));
readfile("$filename");
But if they are using a modern browser or other download client, and they pause the download and try to resume it, the script (assuming they are still authenticated or whatever) will resend the headers and the file contents from the beginning, thus breaking the download, and basically requiring the file to be redownloaded from the beginning.
How can I enable my script to compensate for paused (and consequentially, resumed) downloads?

Use php's built-in fopen to open the file and then fseek to the right place (based on the range in the request header) and then return the partial file using fpassthru instead of using readfile.
You can find some example code in php under the comments for fread

You need to read the request headers like Range, If-Range, etc then seek to the correct location in the file. Normally a web-server would do this for you on an ordinary file. It's a bit complex but here's something that might get you started:
http://forums.asp.net/t/1218116.aspx
http://www.notes411.com/dominosource/tips.nsf/0/480C4E3BE825F69D802571BC007D5AC9!opendocument
For the second link the code is in part 12

Related

Get zip file from url (PHP)

I have a zip files that I want users to be able to download. The trick is I don't want the users to see what the url is and I don't want to download the file to my server.
So I want users to click a link like this:
http://example.com/download/4
which server-side accesses my S3 bucket with this url:
https://s3.amazonaws.com/my-bucket/uploads/4.zip
I've tried cURL, using S3 methods, and various headers() in my download($file_id) function but can't get this to work. This has to be easy, right?
Your right, its quite easy. Probably you will have to write something like this:
$path = '/my-bucket/uploads/4.zip'; // the file made available for download via this PHP file
$mm_type="application/x-compressed"; // modify accordingly to the file type of $path, but in most cases no need to do so
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Type: " . $mm_type);
header("Content-Length: " .(string)(filesize($path)) );
header('Content-Disposition: attachment; filename="'.basename($path).'"');
header("Content-Transfer-Encoding: binary\n");
readfile($path); // outputs the content of the file
exit();
You set various headers to make your user download the .zip. Afterwards you put your file into the output buffer with readfile() Afterwards you end your script with exit() for security's sake. This should work for you! Remember to change the path to your file.
Thanks #Xatenev for the help. This is actually what worked for me:
$path = '/my-bucket/uploads/4.zip'; // the file made available for download via this PHP file
$mm_type="application/zip"; // modify accordingly to the file type of $path, but in most cases no need to do so
header("Content-Type: " . $mm_type);
header('Content-Disposition: attachment; filename="'.basename($path).'"');
readfile($path); // outputs the content of the file
exit();

How to control the browser progress update in the download process?

I wrote a download script in PHP as specified below, my script is downloading the files correctly, but I am feeling that the browser(chrome) progress bar is not getting updated properly in regular intervals.
My file is of size 320MB, while downloading that file the progress is getting updated randomly as "11MB, 76MB, 200Mb & 320MB" or "70MB & 320MB" etc.
In most of the sites download progress update is happening in constant chunks like after every MB, so I want to know how we can control the progress update intervals, may be by sending some extra headers or something else.
I want to improve the user experience by updating the progress in constant intervals, so anybody please help me to handle this situation in a proper way.
// HTTP Headers for ZIP File Downloads
// http://perishablepress.com/press/2010/11/17/http-headers-file-downloads/
// file variables
$filename = "Movie Tunes.zip";
$filepath = "files/";
// http headers for zip downloads
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: public,must-revalidate, post-check=0, pre-check=0");
header("Content-Description: File Transfer"); // MIME
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$filename."\"");
header("Content-Transfer-Encoding: binary"); // MIME
header("Content-Length: ".filesize($filepath.$filename));
ob_end_flush();
#readfile($filepath.$filename);
Thanks,
Siva
No, you cannot influence when/how the browser updates its download progress bar.

Save pdf to local server

I am creating a PDF file from raw binary data and it's working perfectly but because of the headers that I define in my PHP file it prompts the user either to "save" the file or "open with". Is there any way that I can save the file on local server somewhere here http://localhost/pdf?
Below are the headers I have defined in my page
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Type: application/pdf");
header("Content-Disposition: attachment; filename=$filename");
header("Content-Transfer-Encoding: binary");
If you would like to save the file on the server rather than have the visitor download it, you won't need the headers. Headers are for telling the client what you are sending them, which is in this case nothing (although you are likely displaying a page linking to you newly created PDF or something).
So, instead just use a function such as file_put_contents to store the file locally, eventually letting your web server handle file transfer and HTTP headers.
// Let's say you have a function `generate_pdf()` which creates the PDF,
// and a variable $pdf_data where the file contents are stored upon creation
$pdf_data = generate_pdf();
// And a path where the file will be created
$path = '/path/to/your/www/root/public_html/newly_created_file.pdf';
// Then just save it like this
file_put_contents( $path, $pdf_data );
// Proceed in whatever way suitable, giving the user feedback if needed
// Eg. providing a download link to http://localhost/newly_created_file.pdf
You can use output control functions. Place ob_start() at beginning of your script. At the end use ob_get_contents() and save the content to a local file.
After that you can use ob_end_clean() or ob_end_flush() depending on whether you want to output PDF to browser as well, or you would redirect user to some other page. If you use ob_end_flush() make sure you set the headers before flushing the data.

Hide download file location. - Redirect download

I am selling a digital product and want to hide the true location of the download.
So I'm using a redirect script like this:
protected function redirectDownload ($realfilename) {
ob_start();
$mm_type="application/octet-stream";
header("Cache-Control: public, must-revalidate");
header("Pragma: no-cache");
header("Content-Type: " . $mm_type);
header("Content-Length: " .(string)(filesize($realfilename)) );
header('Content-Disposition: attachment; filename="'.$this->fakefilename.'"');
header("Content-Transfer-Encoding: binary\n");
ob_end_clean();
readfile($realfilename);
}
The zip file is always corrupted when I download it, but when I download it directly it is fine.
Does anyone know why this might be?
I think this was working fine on another server, but would need to confirm that.
If I can't solve this, is there any other techniques or services I can use to do this?
Open the downloaded(corrupted) file in an text-editor, i guess there has already been some output before you call the function.
You should better use ob_start() at the begin of your script instead of the begin of the function.

Restriction of 1 download at a time in PHP

I am working on a video site that has different movies and videos which users can stream and download. Now I am being asked to implement a download restriction in such a way that only 1 video can be downloaded at a time. There are two servers: my files and database are on one server and the videos are on the other.
What I am doing for downloading is to send a request from the first server for a file on the other server. If the requested video exists, it is downloaded.
Now I want to restrict the users so that if they are already downloading a video, they cannot download another until the current download completes. Once the current download has completed, the user can download the next video. I have not seen any function that enables a developer to know when the download has completed.
I have a few things in my mind about storing the information of the download time in the database. But storing the time of download is not my requirement.
What is the best way to implement this? Is there an event from which we can detect the download end time? Is there any solution to this? I am using PHP and here is the code that I have used for downloading the file from the second (videos) server. This file sends a request with a file name and full path. The $real_file variable contains the file name along with full path on the second server.
if(file_exists($real_file))
{
header("Pragma: public");
header("Cache-Control: private");
header("Expires: 0");
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: pre-check=0, post-check=0, max-age=0');
header('Content-Transfer-Encoding: binary');
header('Content-Encoding: none');
header("Content-Disposition: attachment; filename=".urlencode(basename($real_file)));
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Description: File Transfer");
header("Content-Length: " . filesize($real_file));
header("Accept-Length: ".filesize($real_file));
$fp = #fopen($real_file, "rb");
while(!feof($fp))
{
$buffer= fread($fp, 8192);
echo $buffer;
}
#flush();
#ob_flush();
die();
}
If you stream the file through a php-script, it would maybe be able to obtain a lock for a specific user (logged in of course) before you start to read the file and outputting to the stream:
(pseudocode)
obtain_lock_somehow();
readfile('yourvideofile.mpg');
release_lock();
I don't know how the script would respond to a closed connection, and it might force the script to end prematurely.
Another option would be to read the file and pass on to the stream in "chunks", and in between every chunk you update the status of the visitors "lock", so that you can identify at which last timestamp the visitor actually downloaded something.
(pseudocode)
while(file_is_not_finished) {
update_lock_status();
pass_thru_buffer();
}
But do note that streaming huge amount of data in a php-script like this is probably not the best way to go, and you might be better off with a native server module for it.

Categories