Zip file downloads, but is invalid? - php

I use this code to enable users to download a zip file:
if(file_exists($filename)){
header("Content-Disposition: attachment; filename=".basename(str_replace(' ', '_', $filename)));
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($filename));
flush();
$fp = fopen($filename, "r");
while (!feof($fp))
{
echo fread($fp, 65536);
flush();
}
fclose($fp);
exit;
}
When the file is downloaded, it only downloads 25,632 kilobytes of data. However the zip file is 26,252 kilobytes ...
Why does the browser get all 25MB but then stop?
I checked the Content-Length header to make sure it was correct and it is...
edit
In firefox, when i download the file, it says 'of 25mb' SO the browser thinks that 25mb is the COMPLETE amount... however, the content-length when echo'd is 26252904?

add this before your code
ob_clean();
ob_end_flush();

Your header('Content-Type ...) calls are useless as only the last one will be sent to the browser.
Downloads are triggered by Content-Disposition: attachment. You should send the actual Content-Type: application/zip if you are sending a zip file.
Finally, your read loop is unnecessary.
Putting it all together, your code should look like this:
if (file_exists($filename)) {
$quoted_filename = basename(addcslashes($filename, "\0..\37\"\177"));
header("Content-Disposition: attachment; filename=\"{$quoted_filename}\"");
header('Content-Type: application/zip');
header('Content-Length: '.filesize($filename));
readfile($filename);
}

Use a single MIME type to represent the data.
In this case using application/octet-stream will do just fine. This is when you dont know the MIME before hand. When you know it, you must put it. Do not use multiple content-type headers.
Usually, when the browser doesn't know how to handle a particular MIME, it will trigger the download process. Further, using Content-disposition: Attachment; .. ensures it.
There exists a simple readfile($filename) which will send out the bytes of the file to the requesting process like below:
header("Content-disposition: attachment;filename=" . basename($filename);
readfile($filename);

I had similar problem. The file downloaded fine in Firefox but not in IE. It appeared that Apache was gzipping the files and IE was not able to ungzip so the files were corrupted. The solution was to disable gzipping in Apache. You can also check if PHP is not gzipping on the fly and disable it too.
For Apache you can try:
SetEnv no-gzip 1
And for PHP, in .htaccess:
php_flag zlib.output_compression on

This answer is by No means a REAL ANSWER.
However i did get it to work... I just set the Content-Length to 30000000. Therefor it thinks the file is bigger than it actually is, and then it downloads it all.
Ugly hack i know, but i couldn't find ANY other way

Related

Asking user to download file in PHP

I am trying to get the browser to prompt the user to download a file. However, after having tried several methods from stack overflow and around the Internet, for some reason all are silently failing. Is it the case that this just isn't possible in modern browsers?
I'm simply wanting the user to download a text (.txt) file from the server. I've tried this code below (and more) to no avail:
header('Content-disposition: attachment; filename=newfile.txt');
header('Content-type: text/plain');
readfile('newfile.txt');
.
header("Content-Type: application/octet-stream");
$file = $_GET["file"] .".txt";
header("Content-Disposition: attachment; filename=" . urlencode($file));
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Description: File Transfer");
header("Content-Length: " . filesize($file));
flush(); // this doesn't really matter.
$fp = fopen($file, "r");
while (!feof($fp))
{
echo fread($fp, 65536);
flush(); // this is essential for large downloads
}
fclose($fp);
I have tried the examples from PHP.NET (none of which are working for me):
http://php.net/manual/en/function.readfile.php
I have the correct permissions set, the file exists and is_readable. I'm now left scratching my head as to why this isn't working. Any help would be great.
I have one solution for you.
Lets assume download.php is the file that downloads the file.
So when the user clicks on the link to download show a confirm dialog, if the user selects yes then re direct the user to download.php or else download will not occur some browsers like chrome starts the download without asking users if they like to download a file or not.

Writing into txt file return empty on IE

I have a code that save the data of a form into a TXT file and it seems like Internet Explorer can't read the file once it is closed.
The file is successfully saved in my folder and when I load it from the FTP I can see my values, but when I do readfile() on the "submit", the donwloadable file is empty from data.
Here is my code :
$fp = fopen("plan_de_concepts/". $nom_du_fichier, "w");
$savestring = "$sujet=*|*=$concept1=*|*=$concept2=*|*=$concept3=*|*=$c1mc1=*|*=$c1mc2=*|*=$c1mc3=*|*=$c1mc4=*|*=$c1mc5=*|*=$c2mc1=*|*=$c2mc2=*|*=$c2mc3=*|*=$c2mc4=*|*=$c2mc5=*|*=$c3mc1=*|*=$c3mc2=*|*=$c3mc4=*|*=$c3mc5=*|*=$c4mc1=*|*=$c4mc2=*|*=$c4mc3=*|*=$c4mc5=*|*=$c5mc1=*|*=$c5mc2=*|*=$c5mc3=*|*=$c5mc3=*|*=$c5mc4=*|*=$c5mc5=*|*=$c6mc1=*|*=$c6mc2=*|*=$c6mc3=*|*=$c6mc4=*|*=$c6mc5";
fwrite($fp, $savestring);
fclose($fp);
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Length: ". filesize($nom_du_fichier));
header("Content-Disposition: attachment; filename=". $nom_du_fichier);
header("Content-Type: application/octet-stream; ");
header("Content-Transfer-Encoding: binary");
readfile('plan_de_concepts/'.$nom_du_fichier);
Works great on Firefox and Chrome but IE return empty.. Is there any ways I could force to donwload the "uploaded" version of the file?
You use filesize($nom_du_fichier) and readfile('plan_de_concepts/'.$nom_du_fichier) (note the folder-prefix). I expect only one of the files exists.
It looks like IE is actually using the Content-Length-header where FF is silently ignoring it.
My Content-length was different of the file exact path. Internet Explorer could not find the file to read it.
It should be noted that in the example:
header('Content-Length: ' . filesize($file));
$file should really be the full path to the file. Otherwise content
length will not always be set, often resulting in the dreaded "0 byte
file" problem.
Source : PHP readfile returns zero length file

Force file download with PHP: corrupted files, have tried several solutions

This has been posted, but I've tried lot of solutions found on SO and more (like this: http://davidwalsh.name/php-force-download)
I basically have this:
{
$filePath = '../public/myfile.png';
$fileName = basename($filePath);
$fileSize = filesize($filePath);
if (!is_file) {
die("File not found");
} else {
header("Content-Description: File Transfer");
header('Content-Type: application/octet-stream');
header("Content-Disposition: attachment; filename= " . $fileName);
header("Content-Transfer-Encoding: binary");
readfile($filePath);
}
}
Files are recognized and downloaded, but .PNGs are empty and .DOCs are corrupted (and Word asks me to fix the file, then it's ok). I have tried also PDFs, and no problem with that.
I trues to put all sort of options (Pragma, Cache-Control, Expires, Content-Length, etc.), still downloaded files but corrupted in some way...
Did you ever had my problem? Please consider I'm on IIS 7.5
Thanks in advance
Open the downloaded files with a plain text editor like Notepad++. At the top you will find a PHP Error notice, it will tell you what's going wrong.
The error is probably "session already send". Then add ob_start(); at the beginning of your script.

headers to force the download of a tar archive

i have a tar archive on the server that must be downloadable through php. This is the code that i've used:
$content=file_get_contents($tar);
header("Content-Type: application/force-download");
header("Content-Disposition: attachment; filename=$tar");
header("Content-Length: ".strlen($content));
unlink($name);
die($content);
The file is downloaded but it's broken and it can't be open. I think that there's something wrong with headers because the file on the server can be open without problems. Do you know how can i solve this problem?
UPDATE
I've tried to print an iframe like this:
<iframe src="<?php echo $tar?>"></iframe>
And the download works, so i'm sure that there's something missing in headers.
I have used this code when I have had to do it:
function _Download($f_location, $f_name){
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Length: ' . filesize($f_location));
header('Content-Disposition: attachment; filename=' . basename($f_name));
readfile($f_location);
}
_Download("../directory/to/tar/raj.tar", "raj.tar");
//or
_Download("/var/www/vhost/domain.com/httpdocs/directory/to/tar/raj.tar", "raj.tar");
Try that.
Don't use file_get_contents() and then echo or print to output the file. That loads the full contents of the file into memory. A large file can/will exceed your script's memory_limit and kill the script.
For dumping a file's contents to the client, it's best to use readfile() - it will properly slurp up file chunks and spit them out at the client without exceeding available memory. Just remember to turn off output buffering before you do so, otherwise you're essentially just doing file_get_contents() again
So, you end up with this:
$tar = 'somefile.tar';
$tar_path = '/the/full/path/to/where/the/file/is' . $tar;
$size = filesize($tar_path);
header("Content-Type: application/x-tar");
header("Content-Disposition: attachment; filename='".$tar."'");
header("Content-Length: $size");
header("Content-Transfer-Encoding: binary");
readfile($tar_path);
If your tar file is actually gzipped, then use "application/x-gtar" instead.
If the file still comes out corrupted after download, do some checking on the client side:
Is the downloaded file 0 bytes, but the download process seemed to take much longer than it would take for 0 bytes to transfer, then it's something client-side preventing the download. Virus scanner? Trojan?
Is the downloaded file partially present, but smaller than the original? Something killed the transfer prematurely. Overeager firewall? Download manager having a bad day? Output buffering active on the server and the last buffer bucket not being flushed properly?
Is the downloaded file the same size as the original? Do an md5/sha1/crc checksum on both copies. If those are the same, then something's wrong with the app opening the file, not the file itself
Is the downloaded file bigger than the original? Open the file in notepad (or something better like notepad++ which doesn't take years to open big fils) and see if any PHP warnings messages, or some invisible whitespace you can't see in your script got inserted into the download at the start or end of the file.
Try something like the following:
$s_filePath = 'somefile.ext';
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'. s_filePath.'"');
header('Content-Transfer-Encoding: binary');
header('Accept-Ranges: bytes');
header('Cache-control: private');
header('Pragma: private');
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header("Content-Length: ".filesize($s_filePath));
$r_fh = fopen($s_filePath,'r');
while(feof($r_fh) === false) {
$s_part = fread($r_fh,10240);
echo $s_part;
}
fclose($r_fh);
exit();
Use Content-Type: application/octet-stream or Content-Type: application/x-gtar
Make sure you aren't echoing anything that isn't the file output. Call ob_clean() before the headers

Force download file with PHP giving empty file

The end goal is for the user to download a .csv file. Right now I'm just testing trying to download a simple text file: test.txt. The only thing in this file is the word "test".
Here is the HTML code for files_to_download.php
Test file: <a href='test.php?file=test.txt'>Test.txt</a>
Code for test.php:
if(!(empty($_GET["file"])))
{
$file_name = $_GET["file"];
$path = "path/to/file";
$fullPath = $path . $file_name;
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: inline; attachment; filename=\"$file_name\"");
header("Content-Type: text/plain");
header("Content-Transfer-Encoding: binary");
readfile($fullPath);
}
I've tried variations of the headers above, adding more and removing others. The above seem to be the most common recommended on this site.
I've also tried changing
header("Content-Type: text/plain");
to
header("Content-Type: text/csv");
and get same results: empty .txt or .csv file.
The files are not empty when I open them directly (file browser) from the server. I've checked the permissions of the files and they're both 644, so the entire world can at least read the files. The directory is 777.
Is there a configuration on the Apache server I need to specify that may not be or am I missing something above.
Thanks for looking!
In most cases the path is wrong
Read the text file, then echo the text out after your header() calls.
Here's how I have my csv download set up:
//downloads an export of the user DB
$csv = User::exportUsers();
header('Content-disposition: attachment; filename=userdb.csv');
header('Content-type: text/csv');
echo $csv;
Where exportUsers() creates the csv data. You can easily just replace $csv with the contents of your text file, then echo it out.
And as far as your text file, you can use file_get_contents() to get the contents of your file into a string. Then echo that string.
Try setting the content length of the file:
header('Content-Length: ' . filesize($file));
Also, please have this in mind: file inclusion
In my case, the path was correct. And the download-forcing was working on windows, but not mac.
I figured out after few tests that the header Content-Length was failing. I was using the function filesize on a full url, like :
$url_my_file = "http://my-website.com/folders/file.ext";
header('Content-Length: '.(filesize($url_my_file)));
I replace it by
$url_my_file = "http://my-website.com/folders/file.ext";
$headers = get_headers($url_my_file, 1);
header('Content-Length: '.($headers['Content-Length']));
And ... It's working now :)

Categories