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.
Related
I am trying to add the PHPPowerPoint to one of my tools. I add all the file that PHPPowerPoint needs and write the download link to the right page but when I try to download it, it say to me that I don't have the permit to access to the file.
I tried to change the permit manually but nothing change, also beacuse everytime PHP create a new file with the default permit.
I tried to use chmod on it but nothing change.
I tried also chgrp and chown to change the owner (that is "daemon").
It's weird because when I use it out from the tool, with only the code to create the PP file everything works also with this permit.
The tool where I want to add the PP file download was coded with codeigniter.
Here's how to write a PHPPowerPoint object to the browser and have the user download it. It doesn't ever write to the file system, so there's no need for write permissions.
$ppp = new PHPPowerPoint;
// create the powerpoint, adding slides, content, etc.
// ...
// done
// set up the writer
$pppwriter = PHPPowerPoint_IOFactory::createWriter($ppp, 'PowerPoint2007');
// tell the browser a powerpoint is coming
header('Content-Type: application/vnd.openxmlformats-officedocument.presentationml.presentation; charset=binary');
// make sure it downloads as $filename
header("Content-Disposition: attachment; filename={$filename}");
// output the powerpoint file data
$pppwriter->save('php://output');
you can use the download helper
The Download Helper lets you download data to your desktop.
Loading this Helper
This helper is loaded using the following code:
$this->load->helper('download');
The following functions are available:
force_download('filename', 'data')
Generates server headers which force data to be downloaded to your desktop. Useful with file downloads. The first parameter is the name you want the downloaded file to be named, the second parameter is the file data. Example:
$data = 'Here is some text!';
$name = 'mytext.txt';
force_download($name, $data);
If you want to download an existing file from your server you'll need to read the file into a string:
$data = file_get_contents("/path/to/photo.jpg"); // Read the file's contents
$name = 'myphoto.jpg';
force_download($name, $data);
I done the functionality for downloading .pdf file as it is in core php,
Here is a sample code for it
$path = "path of your file";
$fullPath = $path.$_GET['fnm'];
if ($fd = fopen ($fullPath, "r")) {
$fsize = filesize($fullPath);
$path_parts = pathinfo($fullPath);
$ext = strtolower($path_parts["extension"]);
switch ($ext) {
case "pdf":
header("Content-type: application/pdf"); // add here more headers for diff. extensions
header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\""); // use 'attachment' to force a download
break;
default;
header("Content-type: application/octet-stream");
header("Content-Disposition: filename=\"".$path_parts["basename"]."\"");
}
header("Content-length: $fsize");
header("Cache-control: private"); //use this to open files directly
while(!feof($fd)) {
$buffer = fread($fd, 2048);
echo $buffer;
}
}
fclose ($fd);
exit;
Just change file extension pdf to ppt(wherever pdf extension occurs).
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.
After some testing with the code below I've come to this conclusion:
The script works fine for AVI files in both firefox and chrome (if I change the content type to video/avi of course)
The script works fine for MKV files in Chrome. Firefox throws an error message in my face though (see below)
I've downloaded a 20 mb big test file (test7.mkv) from http://matroska.org/downloads/test_w1.html to test with a smaller file. Strangely Firefox can download that file and it seems to work. However if I try it on a 6 gb big file I get the firefox error you see below
Edit: Downloaded a 700 mb file, that didnt play at all in SM player (exitcode 1) but played fine in VLC player. I'm currently looking for a way to find how and where the file might be damaged.
My obvious question is: what am I doing wrong? How to properly download a mkv file in firefox or better in any common browser for that matter. I cant find anything helpful on google but maybe I'm just looking in the wrong places.
Firefox Error when trying to download a mkv file:
Corrupted Content Error
The page you are trying to view cannot be shown because an error in the data transmission was detected.
The page you are trying to view cannot be shown because an error in the data transmission was detected.Please contact the website
owners to inform them of this problem.
function download($file)
{
$path = $_SERVER['DOCUMENT_ROOT']; //<-- added the relative part after that
$fullPath = $path.$file;
set_time_limit(0);
if ($fd = fopen ($fullPath, "r"))
{
$fsize = filesize($fullPath);
$path_parts = pathinfo($fullPath);
$ext = strtolower($path_parts["extension"]);
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Type: video/x-matroska");
header("Content-Disposition: attachment; filename=\"$file\"");
header("Content-Transfer-Encoding: binary");
header("Content-length: $fsize");
$file = #fopen($fullPath,"rb");
if ($file)
{
while(!feof($file))
{
print(fread($file, 1024*8));
flush();
if (connection_status()!=0)
{
#fclose($file);
die();
}
}
#fclose($file);
}
exit;
}
}
the absolute path to an mkv file on the server
Firefox may be choking on the absolute path in the filename header value.
Try specifying mere a file name instead:
$filename = pathinfo($filePath, PATHINFO_BASENAME);
header("Content-Disposition: attachment; filename=\"$filename\"");
Solution:
I should've checked if the variables I define are set to the expected values. Turns out filesize() returns a signed int with 32 bit.
For now I just took the function that's shown here: https://stackoverflow.com/a/5502328/1232791
An other possible solution would be to not define the content length header. With this solution the client wont know how long his download is going to take though.
I want to download a doc file located at http://confluence.rogersdigitalmedia.com/exportword?pageId=1114407. How can I modify the following code to download a file from that URL??
And can someone please explain what this code does in its current state, what does it download, a file from a directory?
<?php
// place this code inside a php file and call it f.e. "download.php"
$path = $_SERVER['DOCUMENT_ROOT']."/path2file/"; // change the path to fit your websites document structure
$fullPath = $path.$_GET['download_file'];
if ($fd = fopen ($fullPath, "r")) {
$fsize = filesize($fullPath);
$path_parts = pathinfo($fullPath);
$ext = strtolower($path_parts["extension"]);
switch ($ext) {
case "pdf":
header("Content-type: application/pdf"); // add here more headers for diff. extensions
header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\""); // use 'attachment' to force a download
break;
default;
header("Content-type: application/octet-stream");
header("Content-Disposition: filename=\"".$path_parts["basename"]."\"");
}
header("Content-length: $fsize");
header("Cache-control: private"); //use this to open files directly
while(!feof($fd)) {
$buffer = fread($fd, 2048);
echo $buffer;
}
}
fclose ($fd);
exit;
// example: place this kind of link into the document where the file download is offered:
// Download here
?>
This code is supposed to download files through PHP. Usually, it's used to hide the directory containing the downloads, or to download files which were otherwise inaccessible because the files are outside the web root. Another use for such a script is to offer downloads for authorized users, you'd have to put an authentication check in the script.
If the file has a PDF extension, the download is offered as with the PDF mimetype, so browsers can open it in a PDF viewer. Other files are offered as binary files which can be saved.
Do not use this script "as-is". It contains a huge security vulnerability which allows an attacker to view arbitrary files on your system (Path traversal). Replace line:
$fullPath = $path.$_GET['download_file'];
with the following to make it a bit more secure:
$fullPath = $path . basename($_GET['download_file']);
Even better: implement whitelisting by allowing filenames within an allowed character set and rejecting other invalid filenames.
Downloading an external file is as easy as following the example of cURL:
<?php
$ch = curl_init("http://www.example.com/");
$fp = fopen("example_homepage.txt", "w");
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
fclose($fp);
?>
Since I've no clue about the URL of your download, I'll leave the original URLs and filenames from the PHP example.
This code is something you put on your own server to allow people to download files through PHP. Usually you'd add some authentication code in there so PHP can accept/reject the user before downloading.
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.