This could works perfectly in passing a zipped file back to the client. But the little snippet at the end unlinking the file doesn't seem to be working?
I am assuming that the fpassthru locks the file, so the unlink cannot do anything..
Is there a callback option available.. something to remove the file after the client has received it?
// we deliver a zip file
header("Content-Type: archive/zip");
// filename for the browser to save the zip file
header("Content-Disposition: attachment; filename=$guideName".".zip");
$filesize = filesize($zip_file);
header("Content-Length: $filesize");
// deliver the zip file
$fp = fopen($zip_file,'r');
echo fpassthru($fp);
// clean up the tmp zip file
unlink($zip_file);
exit();
While the file is opened, it's locked and cannot be deleted.
fclose($fp);
unlink($zip_file);
Also make sure the file is writable by the www-user / fpm script owner (=> chmod).
To debug this, I suggest a combination of error reporting, output buffers and mail:
ob_start();
error_reporting(E_ALL);
fclose($fp);
unlink($zip_file);
$debug = ob_get_contents();
mail('you#server', 'error in zip upload', var_export($debug, true));
Another sidenote is the concatenation here:
("Content-Disposition: attachment; filename=$guideName".".zip")
Properly:
("Content-Disposition: attachment; filename=" . $guideName . ".zip")
You could also use file_get_contents() which is basically the same as fopen, fpassthru, fclose in once:
header("Content-Length: $filesize");
// deliver the zip file
echo file_get_contents($zip_file);
// clean up the tmp zip file
unlink($zip_file);
You are ceating a file handle with the call to fopen, which is what will be locking the file. You need to ensure that you call fclose before unlink. E.g.
$fp = fopen($zip_file,'r');
echo fpassthru($fp);
fclose($fp);
unlink($zip_file);
First of all there is an error in your code which will most likely corrupt the file you are sending:
Return values
If an error occurs, fpassthru() returns FALSE. Otherwise, fpassthru()
returns the number of characters read from handle and passed through
to the output.
Therefore your code should look like:
// deliver the zip file
$fp = fopen($zip_file,'r');
fpassthru($fp);
Notice there is no echo before fpassthru.
A MUCH more reliable solution would be to run a separate script to clean the folder where these files are stored every x minutes/hours/days.
If the file is open and you delete it, it is still open and can be read from, but no longer accessible by file name. When script terminates and file handle is closed the space taken by the file will be freed.
The other possible way is using session shutdown handler.
Related
We are receiving file data from a third party API call that returns the content type in one variable and the file contents in another. The file contents are "garbage code." For example:
$doc->fileContents = "A�[�j쎪A����Kb����m��= ....";
$doc->fileType = "application/msword";
I need to be able to provide my users a clickable method for downloading the file, but simply writing the contents to a file and forcing a download with that content type simply opens a file containing all that garbage... not the real file contents.
EDIT: Per Patrick Q's request, I did it the standard fwrite way:
$fp = fopen($filename, 'w'); // filename is generated elsewhere
fwrite($fp, $doc->fileContents);
fclose($fp);
So the question is, how do I create a real, usable file out of this garbage?
The data looks like binary file data. There's no need to write it to a file first, just make sure you set the Content-Transfer-Encoding header to binary when you send it to the browser for downloading.
I'm assuming you already have a way of mapping $doc->fileType to a filename with the proper extension, I'll just hardwire .doc here since your example is an application/msword document.
// your logic here to determine the filename sent to the browser
$filename = "file.doc";
// set the minimum appropriate HTTP headers
header('Content-Type: ' . $doc->fileType);
header('Content-Length: ' . strlen($doc->fileContents));
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Transfer-Encoding: binary'); // this is an important one
// send the file and stop
echo $doc->fileContents;
exit;
If you are writing it to file first, make sure you open it in binary mode:
$fp = fopen($filename, 'wb');
fwrite($fp, $doc->fileContents);
fclose($fp);
I am using php ZipArchive to create an zip file on-the-fly and send it back to the user. I temporarily store the zipped file in a folder above document root and then send it back with the code
header('Content-type:application/zip');
header('Content-Disposition: inline; filename="'.("file.zip").'"');
header("Content-Transfer-Encoding: binary");
header("Content-Length:".filesize($file));
$fh = fopen($file,'rb');
fpassthru($fh);
after having first issued a
$zip->close()
to ensure that that the file isn't open. The issue I have run into is this - the stored zip file is a valid archive which I can open in Windows 7, 7Zip, WinZIP etc. However, when I send the file down with the code above it ends up with an 0xD 0xA pair at the start of the file which is enough to render it corrupt. I cannot figure out where those characters could be coming from. Is this a known bug with fopen/fpassthru? Any help would be much appreciated.
I found when removing the header("Content-Length:".filesize($file)); line it fixed my very same problem...
After many tries to download zip file, the solution was:
$result = create_zip($files_to_zip,$fileZip,true,$path_parts['dirname']);
ob_clean();
ob_end_flush(); // more important function - (without - error corrupted
zip)
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header('Content-Type: application/zip;\n');
header("Content-Transfer-Encoding: Binary");
header("Content-Disposition: attachment; filename=\"".basename($fileZip)."\"");
readfile($fileZip);
unlink($fileZip);
exit();
What does your full script look like?
Generally speaking you should remove any closing PHP tags at the end of your script files, as it's probably output coming from the end of your script, or an included script.
Thank you #_on !
You helped me with this information.
But instead of removing header ("Content-Length:". Filesize ($ file)) ;, I inserted a line break "\\n", leaving:
header ("Content-Length:". filesize ($ file). "\\n");
And the file generated its size to assist in the download
Okay then.
Tks
I have following PHP Script for downloading the zip files:
header("Content-length:".filesize($archive_file_name));
header('Content-Type: application/zip'); // ZIP file
header('Content-Disposition: attachment; filename="downloadpackage.zip"');
header('Content-Transfer-Encoding: binary');
readfile($archive_file_name);
exit();
But it doesn't force the download the zip file instead, it shows the lots of unusual characters like below:
PK[�FA����#�X$D:\voice_06101243342pm014771957.wav�|�s]�y���b�CI\��{2m��M'�Eibu2\#B��܋{� 6b# $H,\#��:��Z�(q�����ә���Y��q�����,� ���� d��y��O�tp$PĽg��w}��}�r֯[7�u�"w���|o�7/Z��ˋ^Z������Ң�ZTZ\S|N����c�X8��c�X8��c�X8��c�X8��c�X8���۲,��a�oA*=ە���do:��|ˉ'rb�|�L2�*^�:q}/��\�s�Z?��G�[��=ϗ?
I have also placed the below code before the headers function :
if (headers_sent())
{
// HTTP header has already been sent
return false;
}
// clean buffer(s)
while (ob_get_level() > 0)
{
ob_end_clean();
}
This ignore to display those character and zip is also not force to download. I couldn't figure out the problem, please help me, it will be greatly appreciated.
PK[�FA����#�X$D:\voice_06101243342pm014771957.wav�|
�s]�y���b�CI\��{2m��M'�Eibu2\#B��܋{� 6b#
$H,\#��:��Z�(q�����ә���Y��q�����,� ���� d��y��O�tp$PĽg��w}��}
�r֯[7�u�"w���|o�7/Z��ˋ^Z������Ң�ZTZ\S|N����c�X8��c�X8��c�X8��c�X8��c�X8���۲,��a�oA*
=ە���do:��|ˉ'rb�|�L2�*^�:q}/��\�s�Z?��G�[��=ϗ?
This is your zip file.
You are a receiving a zip file and there's nothing wrong, you just need to correct your php processing script because apparently it is processing it as plain text/string.
Note the PK at the start, zip files start with 'PK` data. Test yourself by opening any zip file with a hex editor.
Try to add this to your header:
header('Content-Description: File Transfer');
Here is the simple function I use to download files:
function _download ($file_name) {
$src = fopen($this->download_path . $file_name,'r') or die("\n\n ========= Could not download $file_name\n");
$dest = fopen($file_name,'w');
stream_copy_to_stream($src, $dest);
}
Im trying to make a script to download images from my localhost. Just for a schoolproject.
I get the filename via the url ("$_GET['file']"). Then i run this script. Every time the file is damaged and can't be viewed. I want to download images, but when i tried a word document it also was damaged. This is my code:
<?php
//get file
$file = $_GET['file'];
//set path of file
$path = $_SERVER['DOCUMENT_ROOT']."/blackbox/mediafiles/";
$fullPath = $path.$file;
if ($fd = fopen ($fullPath, "r")) {
$path_parts = pathinfo($fullPath);
header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\""); // fore a download
header("Content-type: application/octet-stream");
header("Content-Disposition: filename=\"".$path_parts["basename"]."\"");
}
header("Cache-control: private"); // open files directly
while(!feof($fd)) {
$buffer = fread($fd, 2048);
echo $buffer;
}
fclose ($fd);
exit;
?>
Anybody has and idea what goes wrong?
Thanks in advance!!
Try opening the file explicitly in binary mode:
if ($fd = fopen ($fullPath, "rb")) {
As the documentation on fopen states:
Windows offers a text-mode translation flag ('t') which will
transparently translate \n to \r\n when working with the file. In
contrast, you can also use 'b' to force binary mode, which will not
translate your data. To use these flags, specify either 'b' or 't' as
the last character of the mode parameter.
The default translation mode depends on the SAPI and version of PHP
that you are using, so you are encouraged to always specify the
appropriate flag for portability reasons. You should use the 't' mode
if you are working with plain-text files and you use \n to delimit
your line endings in your script, but expect your files to be readable
with applications such as notepad. You should use the 'b' in all other
cases.
If you do not specify the 'b' flag when working with binary files, you
may experience strange problems with your data, including broken image
files and strange problems with \r\n characters.
You can use readfile instead of manually reading file and outputting.
Also, please note, that $_GET['file'] can contain '../' and open any file, which is a security risk. Use the basename function (if all files are in the same directory) or restrict access to files outside the mediafiles directory
<?php
//get file
$file = $_GET['file'];
//set path of file
$path = $_SERVER['DOCUMENT_ROOT']."/blackbox/mediafiles/";
$fullPath = $path. basename($file);
if (is_readable($fullPath) {
$path_parts = pathinfo($fullPath);
header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\""); // fore a download
header("Content-type: application/octet-stream");
header("Content-length: " . filesize($fullPath));
header("Content-Disposition: filename=\"".$path_parts["basename"]."\"");
header("Cache-control: private"); // open files directly
readfile($fullPath);
}
exit;
?>
At first its not your answer. But you should really look at Security in PHP. On Your script you can access all files over the GET Parameter and DOCUMENT_ROOT.
Don't trust a user.
You should filter your variables at first or map them with a known list of files before deliver them to a user.
PHPSec
Either something is throwing an error from PHP, or you have leading whitespace before the opening tag in the file (<?php).
Do two things:
Make sure the < of <?php is the first character in the file. Make sure your script doesn't have a byte order marker in it.
Add these lines to the top of the script: error_reporting(0); ini_set('display_errors', 0);
Note that if disabling errors fixes your problem it means that there is an error which needs to be fixed, it is not a final solution to the problem!
Using readfile() is also shorter, safer and more efficient than opening a file pointer and looping it.
I have a PHP file that generates xls files using the module found at http://pear.php.net/package/Spreadsheet_Excel_Writer/
I can create the sample document just fine and when I open it, it looks fine.
My next step it to turn it into a downloadable link. To do that, I did this:
$mimeType = "application/vnd.ms-excel";
$file_name = "test.xls";
$file_path = "/tmp/".$file_name;
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header('Content-Type: application/' . $mimeType);
header('Content-Length: '.$size);
header("Content-Disposition: attachment;filename=$file_name ");
header("Content-Transfer-Encoding: binary ");
// open the file in binary read-only mode
// display the error messages if the file can´t be opened
$file = & fopen($file_path, 'rb');
if ($file) {
// stream the file and exit the script when complete
fpassthru($file);
exit;
} else {
echo $err;
}
When I download the file however, it contains a lot of garbage data both in Excel and OpenOffice. The diff says that then binary file in the /tmp folder and the downloaded file are different from each other. I'm guessing that it has something to do with the headers or with fpassthru but I haven't had much luck with debugging the issue.
Any ideas on what the problem is?
The multiple Content-Type headers are uncessary. You're essentially saying that the file is a muffin and a pizza and a ford taurus all at the same time. All you need is the application/octet-stream version, unless you want to serve up the exact mime type.
As well, is there any reason you're trying to turn the file handle returned by fopen() into a reference?
Try something simpler:
<?php
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment;filename=$file_name");
readfile("/tmp/test.xls");
exit();
?>
and see if that does any better.
Just make sure that you don't send ANYTHING out to the browser BEFORE the actual file content gets send.
It might just be some php 'error' or even 'notice' that Spreadsheet_Excel_Writer is producing and you don't even see. Or it might be a closing '?>' tag thats followed by s simple space or newline.
I had a similar error where the file that was generated inside the web folders were working. However the delivery using header('...') gave me corrupt files. This was due to a single space at the end of one php file after the closing '?>' tag.
I am using the same library and I just discovered that the files in the library itself are creating the whitespace.
Solution: In the following files remove the whitespace at the end of the file, or remove the ?> closing tag at the end.
Files to edit (all files in the Spreadsheet_Excel_Writer package):
Writer.php
Workbook.php
Worksheet.php
PPS.php
Parser.php
OLE.php
Parser.php
File.php
BIFFWriter.php
Validator.php
Root.php
Add the following code at the top of the page where the excel file is generated
ob_clean();
This would clear all the gibberish data.Also check for any echo statements.If echo statements are present, remove them. The data should always present in format specified by excel package.