delete a file after download in codeigniter - php

I want to remove my file from downloads directory after a user has downloaded it. But it is not deleted. I am using Codeigniter download_helper for downloading the file.
Below is my code:
Controller
public function download($key,$id)
{
$link=$this->article->download($key,$id);//get record from database via article model
if(empty($link))
{
show_404();
}
force_download(DOWNLOADS_PATH.$link->file_name, NULL);//download file
$this->article_lib->remove_downloaded($link->file_name);//user has downloaded so now delete this
}
Library
public function remove_downloaded($file_name)
{
if(file_exists(DOWNLOADS_PATH.$file_name))
{
unlink(DOWNLOADS_PATH.$file_name);
}
}

You can find out why it wont be deleted in system/helpers/download_helper.php, there is an exit, so all code you have after force_download() will never be executed.
//File: system/helpers/download_helper.php
// From line 135
// Generate the server headers
header('Content-Type: '.$mime);
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Expires: 0');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.$filesize);
header('Cache-Control: private, no-transform, no-store, must-revalidate');
// If we have raw data - just dump it
if ($data !== NULL)
{
exit($data);
}
// Flush 1MB chunks of data
while ( ! feof($fp) && ($data = fread($fp, 1048576)) !== FALSE)
{
echo $data;
}
fclose($fp);
exit;
}

Sorry I was not reading, but the problem with your code is that it is missing the closing )
public function remove_downloaded($file_name)
{
if(file_exists($file_name)) // here is the problem
{
unlink($file_name);
}
}
Also the way the force download works is that anything after will not run. I would suggest using an ajax call after the controller.
UPDATE:
But as you have mentioned in your comment, you can delete the file before creating it as mentioned in this post Unlink after force download not working Codeigniter

I used ignore_user_abort(true) function (docs) to solve this issue, and it worked for me.
Try below code,
public function download($key, $id)
{
$link = $this->article->download($key, $id);//get record from database via article model
if (empty($link)) {
show_404();
}
force_download(DOWNLOADS_PATH . $link->file_name, NULL);//download file
ignore_user_abort(true); // Set whether a client disconnect should abort script execution
if (connection_aborted()) {
$this->article_lib->remove_downloaded($link->file_name);//user has downloaded so now delete this
}
}

While downloading you are passing the full path of the file you want to download but while deleting it, i mean while unlinking it you are passing only filename.
So please do try:
public function download($key,$id)
{
$link=$this->article->download($key,$id);//get record from database via article model
if(empty($link))
{
show_404();
}
force_download(DOWNLOADS_PATH.$link->file_name, NULL);//download file
$this->article_lib->remove_downloaded(DOWNLOADS_PATH.$link->file_name); //Included full path
}
Hope this helps.

just replace in controller function
this:
$this->article_lib->remove_downloaded($link->file_name);
with:
$this->article_lib->remove_downloaded(DOWNLOADS_PATH.$link->file_name);
or maybe with:
$this->article->remove_downloaded(DOWNLOADS_PATH.$link->file_name);

Make sure that you are deleting a file not the directory. if not sure
then try this. it will delete the file and directory also
function rrmdir($dir) {
if (is_dir($dir)) {
$files=scandir($dir);
foreach ($files as $file)
if ($file != "." && $file != "..") rrmdir("$dir/$file");
rmdir($dir);
}
else if (file_exists($dir)) unlink($dir);
}

Related

how can I download my file residing in local repository

I am new to CodeIgniter and was working on downloading a file. However, I want to download a file that resides on my local machine, I am able to locate the file by providing the path, also the file gets downloaded with a File type, However, I want my file to be in .csv format
Here goes my Controller's download function:
public function download()
{
$state_id=$this->input->post('state'); // gets me the state-id from viw's dropdown
$this->load->helper('download'); // Load download helper
$file="C:\\Users\usernew\\Desktop\\New folder\\".$state_id.".csv";
$filename=$state_id.'.csv';
if (file_exists($file))
{
$data = file_get_contents($file); //check file exists
force_download($fileName,$data);
}
else{
echo"not working!";
}
}
Where am I going wrong?
The force_download() takes in two parameters, i.e. the file name and the data to be written to that file. As the file already consists of some sample data, therefore, the function goes like this:
public function download()
{
$state_id=$this->input->post('state'); // gets me the state-id from viw's dropdown
$this->load->helper('download'); // Load download helper
$filename=$state_id.'.csv';
$file="C:\\Users\usernew\\Desktop\\New folder\\".$filename;
if (file_exists($file)) //check file exists
{
force_download($file,NULL); //NULL as the file already has data
}
else{
echo"not working!";
}
}
you can use header function instead force_download:
$file="C:\\Users\usernew\\Desktop\\New folder\\".$state_id.".csv";
$filename=$state_id.'.csv';
header("Content-type: text/csv");
header('Content-Disposition: attachment; filename='.$filename);
readfile( $file );

prestashop display attachment inline

I'm creating a custom PDF file containing some product details. I would like to display product attachments that are images. I have changed the attachment controller to
header('Content-Disposition: inline; filename="'.utf8_decode($a->file_name).'"');
When I visit the url through the browser it displays nicely:
http://domain.nl/nl/index.php?controller=attachment&id_attachment=483
But using this url inside an img tag in my PDF template gives:
TCPDF ERROR: [Image] Unable to get image: http://domain.nl/nl/index.php?controller=attachment&id_attachment=483
Does anyone have any idea on how to make this work?
I created a new file next to AttachmentController.php called AttachmentInlineController.php. Contents are slightly different:
<?php
class AttachmentInlineControllerCore extends FrontController
{
public function __construct()
{
$a = new Attachment(Tools::getValue('id_attachment'), $this->context->language->id);
if (!$a->id) {
Tools::redirect('index.php');
}
if (ob_get_level() && ob_get_length() > 0) {
ob_end_clean();
}
header('Content-Transfer-Encoding: binary');
header('Content-Type: '.$a->mime);
header('Content-Length: '.filesize(_PS_DOWNLOAD_DIR_.$a->file));
header('Content-Disposition: inline; filename="'.utf8_decode($a->file_name).'"');
#set_time_limit(0);
readfile(_PS_DOWNLOAD_DIR_.$a->file);
exit;
}
}?>
I removed the hook, changed content-Disposition to inline, and changed the function from postProcess() to __construct. Now if you use:
http://domain.nl/nl/index.php?controller=attachmentInline&id_attachment=483
It will display that attachment inline, and you can use this url in image tags etc.

My PHP generated HTML file is being downloaded twice?

I have a function that creates an HTML page and forces the download. Once the function is run, for some reason it is downloaded to the users computers twice. Here is my code
public function actionDownload($params)
{
// Get the article
$co_model=new Contentorder;
$co_model->load($params['id']);
$co=$co_model->getFields();
$file_name=str_replace(array(' ','.','&',',','\''),'',strtolower($co['article_title'])).".html";
header('Content-Disposition: attachment; filename="'.$file_name.'"');
header('Content-type: text/html');
echo '<!DOCTYPE html><head><title>'.$co['article_title'].'</title></head><body>';
if ($co['id_contenttype']==3){ // Reusable
$lco_model=new Librarycontent;
$lco_model->load($co['id_librarycontent']);
$res=$lco_model->getFields();
if ($res['id_reusabletype']==3) // Youtube
{
echo $res['reusable_file'];
}
}
echo $co['article'].'</body></html>';
exit;
}
It successfully forces the download on the dynamically created HTML file, but it is downloaded twice for some reason when I only want the file to be downloaded once.
EDIT ------ Here are the headers

Semicolon splits fields when export as a CSV file?

I exported the reviews from my magento website in csv format. Reviews contains :,;,?,! etc. Normally reviews are well in fields. When the review contain ; ,it's get splitted to next field. (I opened the csv file using OpenOffice spreadsheet)
For Example:
Review -> It's nice to use; looks good.
Now output ->
Field A-> It's nice to use
Field B-> looks good
Expected Output
Field A ->It's nice to use; looks good
I used the following header to export as acsv file in php
header('Content-type: application/utf-8');
header('Content-disposition: attachment; filename="file.csv"');
How can I get my expected output?
I would advise tackling the problem from the other side - I believe this is a bone of contention for Magento as some users want the semi-colon.
I would suggest looking at the way you use openoffice - It has been a while but I believe there is an option during the load of the csv called "Separator options" - Change these and all should work well.
The sad thing is if this is the correct answer it is off topic :-( but I hope this helps you achieve your goal!
Thanks,
//P
I'm using this class I've made for exporting data as a CSV spreadsheet. The trick is fputcsv() which should do the formatting right. Then, of course, you must properly import it to OpenOffice (or LibreOffice now), but that's different story.
Maybe it'll help you out.
<?php
class Spreadsheet {
private $grid = array();
/**
* Set cell value at position X, Y
*/
public function setCell($x, $y, $value) {
for($yy=0; $yy<=$y; $yy++) {
if(!isset($this->grid[$yy])) {
$this->grid[$yy] = array();
}
}
for($xx=0; $xx<=$x; $xx++) {
if(!isset($this->grid[$y][$xx])) {
$this->grid[$y][$xx] = null;
}
}
$this->grid[$y][$x] = $value;
}
/**
* final command executed, including "exit".
* Sends CSV to browser.
*/
public function sendAsCsv(/*string*/ $filename) {
header('Content-type: text/csv; charset=utf-8');
header('Content-Language: cs');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header("Pragma: no-cache");
header("Expires: 0");
ob_end_clean();
$outputBuffer = fopen("php://output", 'w');
$this->appendToFile($outputBuffer);
fclose($outputBuffer);
exit;
}
/**
* Appends content to a file (rarely used outside this class)
*/
public function appendToFile(/*resource*/ $fileHandle) {
foreach($this->grid as $val) {
fputcsv($fileHandle, $val);
}
}
}

Yii CSV export to browser getting reamed with log messages instead of data

I am trying to export a CSV to the browser using Yii and the CSV keeps being filled by Yii log messages.
header('Content-Description: File Transfer');
header('Content-Type: application/csv');
header('Content-disposition: attachment; filename=enrollment_course.csv');
$enrollment = Reports::getEnrollment($courseId);
$separator=",";
foreach ($enrollment as $users)
{
$tmp = array(
$users->userid,
$users->firstname,
$users->lastname,
);
echo join($separator, $tmp)."\n";
}
Yii::app()->end();
Any ideas why this doesn't work properly?
yeah, I actually wrote an extension to do that: http://www.yiiframework.com/extension/csvexport/
Basically it amounts to calling exit, take a look at the last few lines of the example:
// options for file, changing delimeter and enclosure
$content = $csv->toCSV('../myfilename.csv', "\t", "'");
Yii::app()->getRequest()->sendFile($filename, $content, "text/csv", false);
// call exit instead of letting sendFile do it, because if you are using
// CWebLogRoute, the onEndRequest event will fire and output your log and not csv file :(
exit();
I would suggest just killing the script after you've output everything. This prevents anything from being added to the output. A simple 'die' should do the trick.
You can use this code in your controller to disable all logging to the browser for all actions:
protected function beforeAction($action)
{
foreach (Yii::app()->log->routes as $route)
{
if ($route instanceof CWebLogRoute)
{
$route->enabled = false;
}
}
return true;
}
source

Categories