Have file data (garbage code) - need to download actual file - php

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);

Related

PHP - After Decrypting, How can I offer the user a OPEN / SAVE file option from the browser without having to save the file first?

So I have a little test app where the user can upload a file (docx,png,jpg and a few others). The file is encrypted and saved to a folder. A DB Table entry is created with the UID of the user and the filename eg
AA1234 Image100.jpg
BB2345 WorkDoc.Doc
This works just fine, the file is encrypted and saved and the table contains the info.
To download a saved, encrypted file, the user is given a select/option from a list of the files applicable to them (eg Image100.jpg by me, AA1234). On submit, the tool should then load the file, decrypt it and open the window to allow the user to OPEN/SAVE the file
My issue is that although this works, its not as tidy as I'd like. The code loads the file, decrypts it, saves it as a decrypted temporary file and then offers the user the OPEN/SAVE window based on this temporary saved file as per below. (it then deletes this temp, decrypted file after).
include('./lib/key.php');
//Get the filename and prepend the folder where the encrypted files are saved
$file = ('encuploads/'.$_POST["fileToDownload"]);
//Read the file and convert it to a string variable
$DocumentString = file_get_contents($file);
//Decrypt the file
$decryption=openssl_decrypt ($DocumentString, $ciphering, $key, $options, $iv);
//Create a new temporary file containing the decrypted info
$decrypt = 'decrypted'.basename($_POST["fileToDownload"]);
$downloadfile = 'encuploads/'.$decrypt;
file_put_contents($downloadfile, $decryption);
//change the headers to output to a "download/save" window for the user
header('Content-Description: File Transfer');
header('Content-Type: application/force-download');
header("Content-Disposition: attachment; filename=\"" . ($_POST["fileToDownload"]) . "\";");
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($downloadfile));
ob_clean();
flush();
readfile($downloadfile); //showing the path to the server where the file is to be download
//delete the temporary file.
unlink(realpath($downloadfile))
So instead of saving the temp decrypted file - "file_put_contents($downloadfile, $decryption);" and then reading it with the headers set "readfile($downloadfile);" I would like to have the option to straight use the $decryption variable after the header statements
such as echo $decryption with headers set so that it give the user the same OPEN/SAVE options (for the type of file chosen)
Any idea if this is a valid option or any other tidy way of giving the user a download option after decryption (I dont want a DOWNLOAD option ... I would like the popup window)
Thanks

PHP Output MS SQL to a downloadable CSV or Excel file

I'm trying to output data returned by an MS SQL query to an Excel or CSV file with PHP.
I've used the script in this answer and can output the file OK. Without the header lines (at the bottom of my code) it saves in my server's folder structure rather than outputs as a download to the browser.
If I add the header lines, it ouputs to a CSV file but writes the page's HTML to the file rather than the extract from the database! Am I missing a setting somewhere? I tried running the code on a page with no HTML in it (PHP and SQL code only), but it still happens.
// Give the file a suitable name:
$FileName= $PartNumber.".csv";
$fp = fopen($FileName, 'w');
// Connect to MS SQL server; the actual database is chosen in the form
// ConnSQL defined in inc/dbconn/config.php
ConnSQL($idDatabase);
// the query is a biggie; here it is:
require 'inc_sql.php';
// run it through the SQL server
$rstBOM = sqlsrv_query($GLOBALS['ConnSQL'], $sqlBOM);
while ($export= sqlsrv_fetch_array($rstBOM, SQLSRV_FETCH_ASSOC)) {
if (!isset($headings))
{
$headings = array_keys($export);
fputcsv($fp, $headings, ',', '"');
}
fputcsv($fp, $export, ',', '"');
}
// force download csv - exports HTML to CSV!
header("Content-type: application/force-download");
header('Content-Disposition: inline; filename="'.$FileName.'"');
header("Content-Transfer-Encoding: Binary");
header("Content-length: ". filesize($FileName));
header('Content-Type: application/excel');
header('Content-Disposition: attachment; filename="'.$FileName.'"');
fclose($fp);
Any ideas where I'm going wrong please?
You need to output your csv file to the browser simply by putting
readfile($FileName);
At the end of your code after the fclose($fp); function.
Otherwise, browser receives the headers for files, but no content in sent from your PHP code.
You could also generate your csv file on the fly and just echo $csvFileContents; instead. This would prevent server from creating and writing data to file, which could lead to security breaches.
Good luck!

Save file in some specific location using content disposition

Below code works perfectly to download the .html file of a current PHP web page
$filename = 'filename.html';
header('Content-disposition: inline; filename=' . $filename);
header('Content-type: text/html');
Is there any way to save file in some specific location instead the download response.
Ex: if i want to save this filename.html file in location /export/www/html/myproject/var/htmlpages
Tried below logic as alternative also:
ob_start();
file_put_contents('myfile.html', ob_get_contents());
But its generating empty file
Use fopen() to write a file in a specific directory instead of download response like:
<?php
$fp = fopen('data.txt', 'w'); // here you can pass the directory path + filename
fwrite($fp, '1');
fwrite($fp, '23');
fclose($fp);
?>
fwrite() doc

Remove file after fpassthru, is there a callback?

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.

Download CSV file to local Download's folder

I have a php file which converts the form data to csv format and then it should get downloaded automatically to the user's local download folder.
$time = time();
$filename = 'exceldownloads/myreport_'.$time.'.csv';
$file = fopen($filename,'w');
fputcsv($file,$rowexcel);
The above code works fine and stores the csv file in the specified folder in server. But my requirement is to download it to a local folder. I have seen many solutions to the above problem, but they are working only if we know the local destination folder. However, My requirement is to make it downloadable to the end-user local download's folder (whose download location Im unaware of). Is there anyway to get it downloaded on to the end user system without specifically mentioning the destination path.
You can export the output of your web page as an attachment, which will be shown as a download to the user. You can do this by outputting appropriate headers right before you make any output to the user.
Here's an example, that creates a download of a CSV file called foo.csv:
header("Content-type: text/csv");
header("Content-Disposition: attachment;Filename=foo.csv");
After outputting the headers, you just output all of the file's data to the page content.
*Edit: * Here's a working snippet, as requested:
header("Content-type: text/csv");
header("Content-Disposition: attachment;Filename=foo.csv");
echo implode(";", $rowexcel) . "\r\n"; // you should expand this accordingly
alternatively, here is another snippet, based on your code:
$filename = 'myreport'.time().'.csv';
$f = fopen($filename,'w');
fputcsv($f,$rowexcel);
header('Content-type: application/csv');
header('Content-Disposition: attachment;filename="'.$filename.'"');
readfile($filename);
If you are not getting any download, make sure that you don't output anything before the header() calls. Also, make sure that you don't have any UTF8 BOM bytes at the beginning of your PHP file, as these can be misinterpreted for output

Categories