Downloading/Populating CSV File with SendFile - php

I am using the PHP Yii framework and trying to download my file. Currently, it downloads, but the file is populated with the $filePath location. Why doesn't this populate my file correctly?
$getData = // Getting data here.
$fileName = "file.csv";
$filePath = __Dir__ . "/.../exports";
$file = fopen($filePath . '' . $fileName, "w");
fputcsv($file, array_keys($getData[0]));
foreach ($getData as $row){
fputcsv($file, $row);
}
header("Content-Type: text/plain");
header("Content-disposition: attachment; filename = $fileName");
header("Pragma: no-cache");
Yii::app()-> request -> sendFile($fileName, $filePath);

Two things. One, i think you are missing a / between $filePath and $fileName variables. Shouldn't it be something like:
$file = fopen($filePath . '/' . $fileName, "w");
Second, I think the way you are calling sendFile() is incorrect. The second parameter is not expected to be the path to the file. Rather, it is expected to be the actual content that should be sent to the browser. So try changing that line to something like:
Yii::app()->request->sendFile($fileName, file_get_contents($filePath . '/' . $fileName));
Here, in the second parameter we are providing the contents of the file in the path specified by $filePath . '/' . $fileName.

After further research, I was able to solve my error. I needed to add file_get_contents instead of its actual path.
Original:
Yii::app()-> request -> sendFile($fileName, $filePath);
Modified:
Yii::app()-> request -> sendFile($fileName, file_get_contents($filePath . "/" . $fileName));

Related

Why zip file can't be opened after downloaded but fine via manual transfer?

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

After downloading file, it is corrupted

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

Zip file not downloading in PHP

I've implemented the code to Create Zip Folder of Files (from db path) and download zipped folder in PHP. I am using Ubuntu OS.
public function actionDownload($id) {
$model = $this->loadModel($id, 'Document');
$results = array();
$results = $this->createZip($model);
$zip = $results[0];
$size = filesize($zip->filename);
if ($zip->filename) {
header("Content-Description: File Transfer");
header("Content-type: application/zip");
header("Content-Disposition: attachment; filename=\"" . $model->name . "\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . $size);
ob_end_flush();
flush();
readfile($zip->filename);
// To Store User Transaction Data
//$this->saveTransaction();
//ignore_user_abort(true);
unlink($zip->filename);
$zip->close();
// Delete newly created files
foreach ($results[1] as $fl) {
unlink($fl);
}
}
}
public function createZip($model) {
$data = Document::model()->findAll('parent_folder=:id', array(':id' => (int) $model->document_id));
$fileArr = array();
foreach ($data as $type) {
$fileArr[] = $type->path;
}
$filestozip = $fileArr; // FILES ARRAY TO ZIP
$path = Yii::app()->basePath . DS . 'uploads' . DS . Yii::app()->user->id;
//$model->path = trim(DS . $path . DS); // DIR NAME TO MOVE THE ZIPPED FILES
$zip = new ZipArchive();
$files = $filestozip;
$zipName = "USR_" . Yii::app()->user->id . "_" . $model->name . "_" . date("Y-m-d") . ".zip";
$fizip = $path . DS . $zipName;
if ($zip->open($fizip, ZipArchive::CREATE) === TRUE) {
foreach ($files as $fl) {
if (file_exists($fl)) {
$zip->addFile($fl, basename($fl)) or die("<p class='warning'>ERROR: Could not add file: " . $fl . "</p>");
}
}
}
$resultArr = array();
$resultArr[] = $zip;
$resultArr[] = $files;
return $resultArr;
}
The Zip creation code working fine and its creating zip file there but the issue is owner of the file is www-data and file permission is Read-Only.
When I am trying to set chmod($zip->filename, 0777) permission to that zipped folder then its showing an error?
Error 500
chmod(): No such file or directory
In fact file is present there.
If I am trying without chmod() then its showing me error of
Error 500
filesize(): stat failed for /home/demo.user/myapp/public_html/backend/uploads/1/USR_1_kj_2013-12-23.zip
and then its not downloading the zip file.
This is really weird issue I am facing. It seem to be some permission issue of zip file that's why filesize() is not able to perform any operation on that file but strange thing is chmod() also not working.
Need Help on this.
Thanks
If www-data has read permissions, the file permissions are properly set. No chmod required.
You need to call $zip->close() before the download, as only on $zip->close() the file will be written to disk. readfile() will not work unless the file is written to disk.
A smarter way, could be to create the archive in memory only using the php://memory stream wrapper. However this is only an option if you need the archive for a single download only.

How to rename .txt files before being added to zip using ZipArchive()?

I am using ZipArchive() to zip a bunch of .txt files together. Can someone please give me an example how to remove the ID from the beginning of all .txt files before placing them in the zip?
For example:
144-apples.txt
2-oranges.txt
25555-bananas.txt
will be:
apples.txt
oranges.txt
bananas.txt
in the zip.
$name = str_replace(TXT_FILE_DIRECTORY . $group . '/', '', $file);
$zip->addFile($file, $name);
Something like this should work:
<?php
//Your $files array creation logic here
foreach ($files as $file) {
$fileName = preg_replace("/^\d+\-/", "", str_replace(TXT_FILE_DIRECTORY . $group . '/', '', $file));
$zip->addFile($file, $fileName);
}
?>
maybe you can serve the following code:
$this->zipfile->add_dir(FCPATH."files/");
for($u=$ini;$u<=$do;$u++){
$this->zipfile->add_file(implode("",file(FCPATH."files/batchsql_".$u.".txt")), FCPATH."files/batchsql_".$u.".txt");
}
header("Content-type: application/octet-stream");
header("Content-disposition: attachment; filename=zipfile.zip");
echo $this->zipfile->file();

Serving documents outside the web root folder.

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.

Categories