I'm currently making a controller to download files from the server.
It all happens in the index action:
public function indexAction() {
$schuurName = $this->_getParam('storageID');
$fileName = $this->_getParam('fileName');
$name = explode('.', $fileName)[0];
$path = '..' . DIRECTORY_SEPARATOR . 'schuren' . DIRECTORY_SEPARATOR . $schuurName . DIRECTORY_SEPARATOR . $fileName;
if (file_exists($path)) {
$mimeType = mime_content_type($fileName);
header('Content-Type: ' . $mimeType);
header('Content-Length: ' . filesize($path));
header('Content-Disposition: attachment; filename=' . $name . ';');
$resource = fopen($path, 'r');
while (!feof($resource)) {
$chunk = fread($resource, 4096);
echo $chunk;
}
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
}
else {
echo 'file doesn\'t exist';
}
}
So the downloading works right now, I'm testing it with an image of 725 bytes. The problem is.. The image is corrupted so it couldn't be seen/edited. What am I doing wrong in my code?
Thanks!
You should use binary mode. Use the 'rb' flag.
From the php Manual : 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.
http://www.php.net/manual/en/function.fopen.php
Related
I searched and tried a few previous questions/example codes, but couldn't get this to work.
I'm trying to deliver the results to end-users via PHP code. Here is my code.
$varZipFile = $varBaseNameParts[0] . '.' . $varDate . '.zip';
$varZipDir = $varBaseNameParts[0] . '_' . $varDate;
$zip = new ZipArchive();
$zip->open($varZipFile, ZipArchive::CREATE | ZipArchive::OVERWRITE);
$zip->addFile('008.csv');
$zip->addFile('002.csv');
$zip->close(); // mark line xxx
header("Content-Type: application/zip");
header("Content-disposition: attachment;filename=$varZipFile");
header('Content-Length: ' . filesize($varZipFile)); // with or without this line, got the same error
header("Content-Type: application/force-download"); // with or without this line, got the same error
readfile($varZipFile);
I got the .zip file in my browser. However WinZip cannot open it, neither can 7-Zip. WinZip complains that "Error: Central directory not found".
Interestingly, when I manually transfer the file via WinSCP from my server to my Windows machine, I can open the file with either WinZip or 7-Zip. This indicates it works all fine to 'mark line xxx', and problems occurs in the header lines.
TIA!
Your ouput buffer may not be cleared before you try to serve the download. Try to clean the output buffer before serving the download with the ob_clean() function like this:
$zip->close(); // mark line xxx
ob_clean();
Your code is working well on my side, debug this $varBaseNameParts[0] to see if the value is correct or try the below code.
// $name = name of the archive
// $archive_directory = directory to save the archive
// $file_array = list of files
function create_archive($name, $archive_directory, $file_array) {
$target_file = $archive_directory . '/' . $name . date('m-d-y').date('h-i-sa') . '.zip';
$zip = new ZipArchive();
if (count($file_array)) {
if ($zip->open($target_file, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)) {
foreach ($file_array as $file) {
$zip->addFile($file);
}
$zip->close();
chmod($target_file, 0777);
return $target_file;
}
}
return null;
}
function create_download($archive) {
$file_name = basename($archive);
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=$file_name");
header("Content-Length: " . filesize($archive));
readfile($archive);
}
// create the archive file
$archive = create_archive('test', 'archive', ['008.csv', '002.csv']);
if ($archive) {
create_download($archive);
}
I am trying to save files in the DB using BLOB. I know it is not a very good practice but it needs to be done this way.
Anyway the problem that I am facing is that PDF files returned are not readable anymore. But all other files like docx, odt, txt, jpg and so on are working just fine when converted back from blob.
public function store(Request $request)
{
$data = $request->all();
$file = $request->file('file');
$data['data'] = base64_encode(file_get_contents($file));
$data['extension'] = $file->guessExtension();
$data['name'] = $file->getClientOriginalName();
$file = $this->fileRepo->create($data);
return jsend()->success()
->message("Resource Created Successfully")
->data($file->toArray())
->get();
}
public function download($id)
{
$file = $this->fileRepo->find($id);
$randomDir = md5(time() . $file->id . $file->user->id . str_random());
mkdir(public_path() . '/files/' . $randomDir);
$path = public_path() . '/files/' . $randomDir . '/' . html_entity_decode($file->name);
file_put_contents($path, base64_decode($file->data));
header('Content-Description: File Transfer');
return response()->download($path);
}
Where am I going wrong and is there a special way to store the PDFs in a blob field?
It's possible that the headers are being returned incorrectly and this is causing the file to appear unreadable.
Force them by replacing:
header('Content-Description: File Transfer');
return response()->download($path);
With:
$headers = array(
'Content-Description: File Transfer',
'Content-Type: application/octet-stream',
'Content-Disposition: attachment; filename="' . $file->name . '"',
);
return response()->download($path, $file->name, $headers);
You can set your header return like that. you need to use Laravel's response ie.
public function download($id)
{
$file = $this->fileRepo->find($id);
$randomDir = md5(time() . $file->id . $file->user->id . str_random());
mkdir(public_path() . '/files/' . $randomDir);
$path = public_path() . '/files/' . $randomDir . '/' . html_entity_decode($file->name);
file_put_contents($path, base64_decode($file->data));
return response()->download($path)->header('Content-Description', 'File Transfer');
}
I have a function called "viewDoc" which is supposed to go to a folder outside the web root and fetch a file for me. It seams to work ok with images (Jpgs etc) but with PDF's it just outputs a blank gray page as demonstrated here - http://www.tutorplanner.com/userimage/viewdoc/12787622467.pdf
Can anyone see what I'm doing wrong as I've been scratching my head over this for a day!
public function viewDoc($doc) {
$path_parts = pathinfo($_SERVER['REQUEST_URI']);
$file = $doc;
$fileDir = '/var/uploads/';
if (file_exists($fileDir . $file))
{
$contents = file_get_contents($fileDir . $file);
//print_r($contents);
header('Content-Type: ' . mime_content_type($fileDir . $file));
header('Content-Length: ' . filesize($fileDir . $file));
readfile($contents);
}
}
readfile is used with the parameter of a file name - NOT text.
Two examples that would work (file_get_contents):
public function viewDoc($doc) {
$path_parts = pathinfo($_SERVER['REQUEST_URI']);
$file = $doc;
$fileDir = '/var/uploads/';
$filePath = $fileDir . $file;
if (file_exists($filePath))
{
$contents = file_get_contents($filePath);
header('Content-Type: ' . mime_content_type($filePath));
header('Content-Length: ' . filesize($filePath));
echo $contents;
}
}
or (readfile):
public function viewDoc($doc) {
$path_parts = pathinfo($_SERVER['REQUEST_URI']);
$file = $doc;
$fileDir = '/var/uploads/';
$filePath = $fileDir . $file;
if (file_exists($filePath))
{
header('Content-Type: ' . mime_content_type($filePath));
header('Content-Length: ' . filesize($filePath));
readfile($filePath);
}
}
I also added the $filePath variable for you, as there's no reason to concat the string multiple times.
Edit
As extra security, to Yazmat's comment, you can use $file = str_replace(array('..', '/'), '', $doc); as this would remove all references to other directories (however, with the slash it would also remove access to sub directories, so you might want to skip that, dependend on your code and file structure).
You have a great security issue here, anyone can access anything on your server with the function you wrote. I realy advise you to not use it and just put your files (that should be accessible) on a public web directory.
I am trying to get this code to work but for some reason, all the echo's are able to output correct content, but the headers don't seem to want to force the download of my document. What follows is the file I am trying to build for file downloads. It is set to input code like this: downloader.php?f=13&t=doc to download a file that is named 201-xxx.doc or 201-xxx.pdf from one of two folders depending on the users privileges.
All the logic works up to the header info at the bottom. If I comment out the header content type and the header content disposition, then it will read the file into the browser. With either of those lines included, it give me an error that says "Error 6 (net::ERR_FILE_NOT_FOUND): The file or directory could not be found."
<?php
//ob_start();
if ( !defined('__DIR__') ) define('__DIR__', dirname(__FILE__));
define( "TLOJ_FSROOT", __DIR__ . "/" );
define('WP_USE_THEMES', false);
require('./wp-blog-header.php');
$lessonnumber = $_REQUEST['f'];
$type = $_REQUEST['t'];
if ( $lessonnumber < '10' ) { $threedigitlesson = '00' . $lessonnumber; }
elseif ( $lessonnumber < '100' ) { $threedigitlesson = '0' . $lessonnumber; }
else { $threedigitlesson = $lessonnumber; }
$filenamestart = "201-" . $threedigitlesson;
$contenttype = 'application/octet-stream';
switch ($type) {
case 'pdf':
$extension = '.' . $type;
$contenttype = 'application/pdf';
break;
case 'doc':
$extension = '.' . $type;
$contenttype = 'application/msword';
break;
default:
$contenttype = '';
exit("It appears that you are trying to download a file that is not a lesson document. Please contact us if you believe this to be an error.");
}
$filename = $filenamestart . '.' . $type;
$current_user = wp_get_current_user();
//$siteurl = site_url();
$pathroot = TLOJ_FSROOT;
$download_path = $pathroot . "1hoefl4priaspoafr/";
if ( current_user_can("access_s2member_ccap_extendedlessons")) {
$download_path = $download_path . "ex/";
} else {
$download_path = $download_path . "st/";
}
$file_path = $download_path . $filename;
$tlojmemberlength = tlojunlocklessons();
if ( !is_user_logged_in() ) { exit("Please log in to access the file"); }
if ( !current_user_can("access_s2member_ccap_downloadlessons") ) { exit("You don't have access to download the lessons!"); }
if ( $lessonnumber > $tlojmemberlength ) { exit("It appears you are trying to jump ahead! While I am excited at your enthusiam, let's not rush our study time."); }
if ( ($lessonnumber > '195') && (!current_user_can("access_s2member_ccap_lastweek")) ) { exit("Upgrade now to access the downloads for the five bonus lessons!"); }
// build Final File Name
$extendedmessage = "";
if ( current_user_can("access_s2member_ccap_extendedlessons")) { $extendedmessage = " - Extended"; }
$myfinishedlessonname = "Lesson " . $lessonnumber . $extendedmessage . " -- The Life of Jesus Study" . "." . $type;
// echo 'Download Path: ' . $download_path . '<br />';
// echo 'Source/Lesson Number: ' . $lessonnumber . '<br />';
// echo 'File Name: ' . $filename . '<br />';
// echo 'File Type: ' . $type . '<br />';
// echo 'Allowed Lessons: ' . $tlojmemberlength . '<br />';
// echo 'Final File Name: ' . $myfinishedlessonname . '<br />';
// echo 'File Path: ' . $file_path . '<br />';
// echo 'Content Type: ' . $contenttype . '<br />';
// echo 'File Size: ' . filesize($file_path) . '<br />';
if (headers_sent()) { exit("Sorry but the headers have already been sent."); }
ob_end_clean();
if (file_exists($file_path)) {
header('Content-Description: File Transfer');
header('Content-type: ' . $contenttype);
header('Content-disposition: attachment; filename="' . $myfinishedlessonname . '"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: ');
header('Pragma: ');
header('Content-Length: ' . filesize($file_path));
flush();
ob_clean();
readfile($file_path);
exit;
} else { exit("No file present."); }
?>
Please help as I have been at this all day and am confused to no end why this won't work. Filesize() pulls the correct length so I know there is a file in the path that I am looking at. (I am also new to PHP, so if there is something that I am missing, please share.)
Thanks in advance!
If it's a big file, it cannot be sent with readfile. Try to use this:
$handle = fopen($file_path, 'rb');
$buffer = '';
while (!feof($handle)) {
$buffer = fread($handle, 4096);
echo $buffer;
ob_flush();
flush();
}
fclose($handle);
I am not sure why this worked, but I was able to solve this issue by breaking my php file into two pieces. Piece 1 loads WordPress and performs the logic validation. Then file 1 passes the information over to file 2 to do the download logic and write the header information.
Like I said, I am not sure why this worked, however a friend who knows PHP better than I do said that sometimes if the script takes too long to process then the headers won't take. It is possible that WordPress was hanging the script too long for these headers.
Hopefully this explanation will help someone else who is having this difficulty.
If you are trying to force browser to download the file with the use of
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="your-file.jpg"
but Chrome gives you ERR_FILE_NOT_FOUND and Firefox also fails with "Not found" (strangely Opera seems to work) try adding:
header('HTTP/1.0 200 OK', true, 200);
Chrome was telling me: "Error 6 (net::ERR_FILE_NOT_FOUND): The file or directory could not be found."
And Firefox as saying the file did not exist.
Although the same php file was handling a different type of download I was having issues with PNG and ICO, I tried some methods that only displayed the picture but did not prompt for a download box.
Finally I found out thanks to Crazycoolcam, that Wordpress was the issue.
I was including a php to a file I had called "tools.php",
inside of tools.php it had an include to wordpress's main header file,
to remedy the issue I split my tools file into a wordpress version and a non wordpress version and included the wordpress half after it had written the file out.
Just another possibility here as to why its not working. This was the cause for me. Interestingly, file_exists was returning true but no form of serving the file to the public for download was working without having the below set correctly.
PHP has a setting called open_basedir
Make sure this is set correctly relevant to your hosting environment. open_basedir can be edited via php.ini
I was trying to create a directory of private files that could only be accessed when a user logs in. To do this, I used a folder outside the web directory, and then php to access it, if allowed.
Here's an example:
function display_movie($file){
printf("`<video id='movie' width='960' height='416' controls='controls' onerror='fix()'>`<br/>
`<source src='movie.php?file=%s' type='video/ogg; codecs=\"theora, vorbis\"'>
</video>", rawurlencode($file)`);
}
This works great for images, but breaks the media player. Also, I've only tested this locally on a Linux machine.
Any ideas? Thanks.
This is in movie.php...
if(file_exists($fileDir . md5($file) . $ext)) {
$contents = file_get_contents($fileDir . md5($file) . $ext);
}
header('Content-type: video/ogg');
echo $contents;
What is with the <br /> in you're movie.php ?
Did you tryed to see if movie.php echoes the file contents ? ( view source on you're page , then copy the movie source src "movie.php?file=..." and paste it in you're browser , see if it get's the movie out ) .
Allso you need to move the header and echo inside the if statement , if that file doens't exist you can echo a different standard movie :
if(file_exists($fileDir . md5($file) . $ext)) {
$contents = file_get_contents($fileDir . md5($file) . $ext);
} else {
$contents = file_get_contents($fileDir . md5(MOVIE_NOT_FOUND));
}
header('Content-type: video/ogg');
echo $contents;
Where MOVIE_NOT_FOUND is a constant, movie you whant to display if the requested one is not found .
One other thing you could do is enter you're full uri to the movie php in you're source src ( like "http://localhost/some/uri/movie.php?file=..."
From where do you get $fileDir , $file and $ext in you're movie.php ?
Edit: should take care of the file_get_contents problem you have
$file = $fileDir . md5($file) . $ext;
header('Content-type: video/ogg');
//set aditional headers you may whant here
ob_clean();
flush();
if( file_exists($file) )
{
readfile($file);
} else {
readfile($fileDir . md5(MOVIE_NOT_FOUND));
}
exit;