PHP download file: show progress in browser - php

User on my site can download files. Sometimes these files are pretty large and I would like users to see download progress bar at their browsers.
I use the following code to give user a file:
header('Content-Type: text/xml');
header('Content-Disposition: attachment; filename='.$fileName);
header('Pragma: no-cache');
header('Content-Transfer-Encoding: binary');
header('Pragma: public');
header('Content-Length: '.filesize($fullFileName));
header("Content-Description: File Transfer");
$fileHandler = fopen($fullFileName, 'r');
while(!feof($fileHandler)){
echo fread($fileHandler, 2048);
}
fclose($fileHandler);
When I run this script browser(FireFox) freezes for some time, I can see loading colour circle and only after save/open dialog file appears. When I click on "save" button, file almost immediately is downloaded to my computer(while file is quite large - 50 Mb).
I want to have downloading system like on this site. Dialog for save/opean appears immediately when you click on any links. And after you can see downloading progress in your browser.
Are there any special headers to display progress bar in browser? How I should change my code?

I use the code below, and in Firefox it does give me the download time and progress:
// send headers first
header('Content-type: application/octet-stream');
header('Content-Transfer-Encoding: binary');
header('Content-disposition: attachment; filename='.$FileName);
header("Content-Length: ".filesize($Path));
// then use an easy way to flush and end all output buffers
while (#ob_end_flush());
// flush file
readfile($Path);
It should work if you specify Content-Length correctly. The only thing I find mildly weird in your code is the Content-Type: text/xml header. Note that I use Content-type: application/octet-stream.

Related

How to download a very large video file instead of playing in browser with PHP

I have created a movie which I have saved as an MP4 file and uploaded to my server. The file is 4.6 GB. When I have tried to send a link to my family, the video tries to play in their browser.
What I want to do is have them click a link and the file downloads to their computer instead. I have searched endlessly for solutions, but they all keep failing, probably due to the size of the file.
Is anyone able to help with a PHP script that will allow the downloading of such a large file?
Thanks
The easiest solution is to press Ctrl+S, select File>Save or do right click + Save as in the browser when the file starts to load - this will open the Save File dialog.
In case you want to return this file from PHP, you can do that with following script:
<?php
$file = 'video.mp4';
if (file_exists($file)) {
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
exit;
}

Second sequential function stomps the first function

I guess I have to break down and ask for help. (Should have done it 3 days ago!)
Here's what happens...
PHP reads session & post variables, builds a .csv file from a mysql query.
it attempts to open a 'Save As' dialog box and when that's done, jump to another page.
I'm using nested functions but when run, the dialog box seems to get run over and never appears.
separately the functions work fine.
when run, the 'save as' dialog box doesn't wait for user input
Can anyone see what I've done wrong or can you redirect my thinking?
$filename points to the created CSV file on the server
$suggname is a default filename users should see in the dialog box.
The code:
holdit($filename,$suggname);
function holdit($filename,$suggname) {
$fp=#fopen($filename, 'rb');
if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE")) {
header('Content-Type: "application/octet-stream"');
header('Content-Disposition: attachment; filename="'.$suggname.'"' );
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header("Content-Transfer-Encoding: binary");
header('Pragma: public');
header("Content-Length: ".filesize($filename));
} else {
header('Content-Type: "application/octet-stream"');
header('Content-Disposition: attachment; filename="'.$suggname.'"' );
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Pragma: no-cache');
header("Content-Length: ".filesize($filename));
}
fpassthru($fp);
fclose($fp);
jump();
}
function jump() {
header('Location: return_from_csv.php');
}
You are adding lots of headers to your HTTP response. One of those is Location which instructs the browser to redirect. Obviously it is interpreting that as a higher priority than your other headers.
Decide if you want to redirect or serve a file in your response and do one or the other.
I suspect you have misunderstood the Location header. Read this: http://en.wikipedia.org/wiki/HTTP_location
By the looks of things you are trying to serve the CSV file and then redirect to another page. Sorry, you cannot do this. An HTTP response does one thing and one thing only. You might consider opening your link to the CSV file in another window using the target attribute of <a>.

How can I allow users to download pictures (.gif,.png,.jpeg)?

How can I allow users to download pictures saved on the server? The goal is to have the user click on a link and have a specified image to start to download.
Facebook example:
Make the link to another .php page, not the image. Then on that page use the content-disposition header like this:
<?php
// Define the name of image after downloaded
header('Content-Disposition: attachment; filename="file.jpg"');
// Read the original image file
readfile('file.jpg');
?>
From there, you can just add the filename of the image in a get command like
`download.php?filename=file`
then reference that in the file as:
readfile($_GET['filename'].'.jpg')
You need to set a specific header on the response that delivers the image in order to force a download.
Content-Disposition: attachment; filename=myawesomefilename.png
Otherwise it will just load up in browser.
So send that header and then just link to the path that delivers that image with that header.
Send a header to tell the browser to download it like this:
header("Content-type: application/force-download")
Then send them the data for the file itself without any HTML or anything.
This example is snipped from the PHP docs
<?php
$file = 'monkey.gif';
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
}
?>
If by "download pictures saved on the server" you mean "try to make the browser offer a 'save as' dialog box instead of just displaying the image" then you might want to look into using the Content-Disposition: attachment header in the response that serves the image:
Content-Disposition: attachment; filename="thefilename.jpg"
You can set headers in php using the header function.

Prompt user to download PDF file instead of opening

In my project site, if I click on a link, the PDF opens in a new or parent window. Well I want a box to appear that prompts the user to download the file instead of opening it.
Does anyone know of a simple JavaScript onClick event that will do this, in all browsers, with default settings?
My server is PHP based.
Since your edit states that you're using PHP, here's how to do the same in PHP:
<?php
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="downloaded.pdf"');
readfile('original.pdf');
?>
Since you've tagged it .NET, I'd say this is your best solution:
Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "attachment;filename=download.pdf");
Response.WriteFile(Server.MapPath("~/files/myFile.pdf"));
Response.Flush();
Response.Close();
Change the Content-Type to application/octet-stream. You may find however, that some browsers will infer from the file extension that it should open as a PDF with your favorite PDF viewer.
Response.ContentType = "application/octet-stream";
Also, set the following:
Response.AppendHeader( "content-disposition", "attachment; filename=" + name );
You can't do it via javascript, you need server side implementation.
Here's the SO Post which should help:
Allowing user to download from my site through Response.WriteFile()
http://aspalliance.com/259_Downloading_Files__Forcing_the_File_Download_Dialog
If you are getting a corrupted file error try this:
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: private', false);
header('Content-Type: application/pdf');
header('Content-disposition: attachment; filename=' . basename($file));
header("Content-Transfer-Encoding: binary");
header('Content-Length: ' . filesize($file)); // provide file size
header('Connection: close');
readfile($file);
Where $file is the full path or url of the file.

Creating a download link for .jpg file using PHP

This one should be easy, I think. I have a paginated image gallery, and under each image is a small link that says "Download Comp". This should allow people to quickly download the .jpg file (with a PHP generated watermark) to their computer.
Now, I know I can just link straight to the .jpg file, but that requires the user to have the image open in a new window, right click, Save As..., etc. Instead, I want the "Download Comp" link to initiate the download of the file immediately.
PHP.net seemed to suggest using readfile(), so each "Download Comp" link is being echoed as "?download=true&g={$gallery}&i={$image}".
Then at the top of the page I catch to see if the $_GET['download'] var isset, and if so, I run the following code:
if(isset($_GET['download'])) {
$gallery = $_GET['g'];
$image = $_GET['i'];
$file = "../watermark.php?src={$gallery}/images/{$image}";
header('Content-Description: File Transfer');
header('Content-Type: application/jpeg');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: public');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
}
The link takes a lonnnnnnnnng time, and then it brings up a dialog prompt asking you to Open or Save the file, but once you Save and try to open it, it says the file is corrupt and can't be opened.
Any ideas?
Don't set $file to a relative url. The readfile function will try to access the php file on the server. That is not what you want. In your case it looks like the watermark.php file will send the contents you want, so you could possibly just set up the environment it needs and include it.
<?php
if(isset($_GET['download'])) {
$gallery = $_GET['g'];
$image = $_GET['i'];
$_GET['src'] = "{$gallery}/images/{$image}";
header('Content-Description: File Transfer');
header('Content-Type: image/jpeg');
header('Content-Disposition: attachment; filename='.basename($image));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: public');
header('Pragma: public');
ob_clean();
include('../watermark.php');
exit;
}
Another (simpler) way is to modify watermark.php. Add a query parameter to make it send the proper headers to force a download and link to that
...
watermark.php:
<?php
if (isset($_GET['download']) && $_GET['download'] == 'true') {
header('Content-Description: File Transfer');
header('Content-Type: image/jpeg');
header('Content-Disposition: attachment; filename='.basename($src));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: public');
header('Pragma: public');
}
// continue with the rest of the file as-is
Also, you don't need the call to flush(). There should not be any output to send at that point, so it is not necessary.
header('Content-Type: image/jpeg');
Perhaps?
I think you might need to follow the call to readfile() with a call to exit() to make sure nothing else gets written to the output buffer.
This seems like a security issue.
What if someone enters:
$g = '../../../../../../';
$i = '../../sensitive file at root';
How about making .htaccess (if you are using apache) i for the gallery directory serve jpegs up as a download rather than normal.
Also, try file_get_contents() instead of readfile(). I find it works under more circumstances. I would also recommend you use ob_flush() after you output the image data. I've never needed to use ob_clean() or flush() to get this kind of thing to work.
And as Eric said, you may also want to put a call to exit() in there as well for good measure if it still isn't working just in case you are getting some junk data stuck at the end.

Categories