PHP header attachment download corrupts ZIP file - php

I have a file like "983Y4938920820894838947" on my server, and I'd like the user to save it as "subject.zip".
Using header location makes downloading work and the file is not damaged.
Whenever i use the headers with attachment, content type and the new filename, the download zip file is corrupt (I think?).
Whenever I open the ZIP file (e.g. test.zip), it makes a new file called test.zip.cpgz. I assume this is mac's way of saying the file is corrupt.
I'm using the following code
// Download the ZIP File
header("Content-Type: application/zip");
header('Content-Disposition: attachment; filename="' . stripslashes($new_filename) . '"');
readfile($filename);
This makes the file corrupt, while the below code works perfectly (but doesn't change the name):
header("Location: $filename");
I tried other headers without any success. Does anybody have any idea? Thanks!

You are missing a header there, if you dont specify the content length, the browser will not know what the size of the file is:
header("Content-Length: {{replace.with.your.file.size}}");
Maybe this will solve your problems.

The problem was that I had an echo. The echo made the zip corrupt. So it makes sense that it only worked the header location and not the regular headers for changing the filename. Removing the echo fixed it for me.

Related

Downloading an SQlite .db file

Hello currently I am working on a tool that converts an otherwise file-based stored text array(not really important though what it does). It works great and I am able to download and use it if I download the file off the FTP, but when I try to directly download it to the person using the .php file, it says Invalid file. My current code is:
header('Content-Type: application/octet-stream');
header("Content-Length: " . filesize($dbname));
header('Content-Disposition: attachment; filename="object database.db"');
readfile($dbname);
What is the issue? Is the content-type or something else wrong? Or should I be using readfile(I tried this?)
When I download with the above code, the name of the file seems to be correct or whatever but when I attempt to open it with the SQLite3 Browser(it works when I download directly off the FTP) the program displays "Invalid file format". When I use readfile it downloads as "download" with no file extension and when I attempt to open windows says Invalid file. Any help would be awesome.
There's no application/db content type. You should use application/octet-stream instead. Also filename in Content-Disposition is just filename you suggest client to save file as, but nothing more, so you yet needs to send the file content itself to the remote peer.
readfile(....);
die();
should do the work if your data is stored in the file already, otherwise you can just echo() it instead.

PHP: readfile() returns zip file with 0 bytes

I have created php script to allow user to download zip files.
Script looks like:
$filePath = '/path/to/zipfile.zip'; // file is below /public_html/ directory
if(file_exists($filePath)) {
$fileName = basename($filePath);
$fileSize = filesize($filePath); //returns: 26494938
header("Content-Type: application/zip");
header("Content-Length: ".$fileSize);
header("Content-Disposition: attachment; filename=".$fileName);
readfile($filePath);
die;
};
I would expect this script to pass zipfile.zip for downloading. And it does, the save dialog pops up, I am choosing where to save, pressing save and zip file saves. Except one important thing - the file is with 0 bytes, it is completely empty zip file. Original zip file is full with files (around 25Mb). Maybe the size is the problem?
Can someone please help with some advice? Path to file is correct. I suspect that the problem is most likely related to readfile() function and/or zip file size, if so is there some alternative to readfile() that would work? Thank you!
Update: I have tried to use function readfile_chunked as mentioned in this post: "Readfile reads 0 bytes from large file?" - the result is that zip after downloading now have some size (no longer 0 bytes) and all correct content inside it, looks like everything is ok, but the zip is invalid, and cannot be unzipped and opened normally. In Win8 when trying to open downloaded zip file I am receiving this error: The Compresed (zipped) Folder zipfile.zip is invalid.. Of course the original zip file works fine. WinRAR shows this error: zipfile.zip: Unexpected end of archive. It looks the same problem as described in this post, without any working solution.
Try to add ob_end_flush(); before readfile($filePath); . It
The simplest solution is to do away with the php completely, just provide an HTML link to the zip and let your server do the work
also if you really must stick with php to serve this, you have no code there that actually sends the file to the user, readfile doesn't do that it just loads it into the program

Opening downloaded zip file creates cpgz file?

If I make the url for a zip file the href of a link and click the link, my zip file gets downloaded and opening it gets the contents as I expect.
Here's that HTML:
download zip
The problem is I'd like the link to point to my application such that I could determine whether the user is authorized to access this zip file.
so I'd like my HTML to be this:
download zip
and my PHP for the /canDownload page:
//business logic to determine if user can download
if($yesCanDownload){
$archive='https://mysite.com/uploads/my-archive.zip';
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=".basename($archive));
header("Content-Length: ".filesize($archive));
ob_clean();
flush();
echo readfile("$archive");
}
So, I think the problem has to do with the header() code but i've tried a bunch of things related to that based on various google and other SO suggestions and none work.
If you answer my question, it is likely you can answer this question too: Zipped file with PHP results in cpgz file after extraction
The answer in my case was that there was an empty line being output before readfile().
So i added:
ob_end_clean();
readfile($filename);
But you should probably search for the place where this line is being output in your code.
The PHP documentation for readfile says that it will output the contents of a file and return an int.
So your code, echo readfile("$archive");, will echo $archive (btw, the double quotes are meaningless here; you should remove them), and THEN output the int that is being returned. That is, your line should be: readfile($archive);
Also, you should be using a local path (not an http:// link) to the archive.
Altogether:
if($yesCanDownload){
$archive='/path/to/my-archive.zip';
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=".basename($archive));
header("Content-Length: ".filesize($archive));
ob_clean();
flush();
readfile($archive);
}
Lastly, if that does not work, make sure filesize($archive) is returning the accurate length of the file.
Ok, I answered my own question.
The main problem, which I originally didn't make clear, was that the file was not located on my application server. It was in a Amazon AWS s3 bucket. That is why I had used a full url in my question, http://mysite... and not just a file path on the server. As it turns out fopen() can open urls (all s3 bucket "objects", a.k.a. files, have urls) so that is what I did.
Here's my final code:
$zip= "http://mysite.com/uploads/my-archive.zip"; // my Amazon AWS s3 url
header("Content-Type: archive/zip"); // works with "application/zip" too
header("Content-Disposition: attachment; filename='my-archive.zip"); // what you want to call the downloaded zip file, can be different from what is in the s3 bucket
$zip = fopen($zip,"r"); // open the zip file
echo fpassthru($zip); // deliver the zip file
exit(); //non-essential
Another possible answer, I found After much searching, I found that the two possible reasons for a *.zip "unzipping" to a *.zip.cpgz are:
the *.zip file is corrupted
the "unzip" tool being used can't
handle >2GB files
Being a Mac user, the second reason was the cause for my problem unzipping the file: the standard Mac OS tool is Archive Utility, and it apparently can't handle >2GB files. (The file in question for me was a zipped 4GB raspbian disk image.)
What I ended up doing was to use a Debian virtual machine, already existing in Virtual Box on my Mac. unzip 6.0 on Debian 8.2 had no problem unzipping the archive.
You're passing the URL to readfile() like:
$archive = 'https://mysite.com/uploads/my-archive.zip';
While you should pass the path on the server, for example:
$archive = '/uploads/my-archive.zip';
Assuming the file is located in the upload folder.
Additionally try the following headers:
header("Content-type: application/octet-stream");
header("Content-disposition: attachment; filename=file.zip");
In my case, I was trying to create the file in a directory above public_html and the rules of the hosting didn't allow it.

firefox downloads mp3 files without an extension

I am using a Download Class which i found here http://www.phpclasses.org/package/3220-PHP-Serve-files-for-download-with-resume-support.html to let ppl download mp3 files.
It works great in all browsers except Firefox (in all versions).
When i download an mp3 file using this class in firefox, it downloads files without .mp3 extension. what may cause this and how can i fix it ?
It seems that for Firefox to save the file with an extension you must surround the filename with quotes.
header('Content-Disposition: attachment; filename="'.$file.'"');
Please check again filename. If file download have 1 character is spacebar (" ")
Example: "template import.xls". Firefox will be error that without extension in download.
You can function replace(" ", "") to fix this issue with asp.net:
HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=" + reportName.Replace(" ", ""));
Without any code, my guess is that you're not sending the filename as an HTTP header:
header("Content-Disposition: attachment; filename=" . $filename);
You should also be sending the correct MIME type:
header("Content-type: audio/mpeg3");
EDIT: Nevermind, thought I could not download the file at the OP's link and can now see that I was wrong. The script is clearly using these methods, just not sure if it's doing it properly.

Creating and serving zipped files with php

I'm trying to use the following code to create a zip file from a directory and serve it to the user via an http download:
// write the file
file_put_contents($path . "/index.html", $output);
// zip up the contents
chdir($path);
exec("zip -r {$course->name} ./");
$filename = "{$course->name}.zip";
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' .urlencode($filename));
header('Content-Transfer-Encoding: binary');
readfile($filename);
I am able to create the zip file, but downloading it over http is not working. If I download the zip file that's created using an ftp client then Mac's Stuffit Expander unzips the files just fine, but if I download it over http, the mac unzipper creates an endless loop. What I mean by this is say the file I download is called course.zip, then unzipping the file gives course.zip.cpgz and unzipping that file gives course.zip again..and on and on.
Anyone have any ideas?
Thanks!
I had this problem and it turned out the downloaded zip file had a new line inserted at the very beginning.
Solved by using ob_clean and flush functions
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/octet-stream");
header("Content-Disposition: attachment; filename=".basename($archive_file_name));
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($archive_file_name));
ob_clean();
flush();
echo readfile("$archive_file_name");
Re-zipping it every time it is requested is not a good idea. Try doing that only if the ZIP file does not exist already.
If is a volatile file or just a single small file you want to transfer compressed, try using ob_start('ob_gzhandler') instead, simplier, smaller, cleaner. The file is transfered compressed, but it is saved in its original format by the client-side.
Specifying the Content-Length header is needed to allow the downloader to know the end of the file, allowing progress control, detection of corruption of the file and avoiding the hang of the HTTP session (if Connection is in Keep-Alive mode), maybe the lack of this header is the root of the problem.
As suggested by karim79, I'll put my comment as an answer: what happens if you change the MIME type from application/octet-stream to application/zip?
Also, I see you're using a command line zip program, but you don't check for success of the zip, and also don't check if the file exists before attempting to send it out to the end users browser. Try hard coding a file name, manually using zip to guarantee a properly formed zip file, and then see if your code will spit it to your browser properly.
What you're seeing is that the archive utility is not recognizing the zip file as a zip file, and tried to zip up the zip archive itself. The second operation simply unzips the first file created, so never actually opening the file at all. This is due to the zip being corrupted.
It is possible that the browser somehow mangled the zip file (newline conversions anyone?) during the download process. As mentioned, check the mime type and use the php header() to set the correct MIME type (application/zip).

Categories