I've encountered a problem I'm trying to solve for more than two days now: I've built a Website using cakephp and everything is working just fine but I got stuck when I tried to implement download links to files stored under APP_DIR/someFolder/someFile.zip.
How do I set download links to files inside someFolder? I often stumbled over "Media Views" I tried implementing them but so far I've been unsuccessful.
Besides is there no easier way to make files downloadable?
Media Views is deprecated since version 2.3. You should use Sending files instead.
Check out this minimal example in your controller:
public function download($id) {
$path = $this->YourModel->aMagicFunctionThatReturnsThePathToYourFile($id);
$this->response->file($path, array(
'download' => true,
'name' => 'the name of the file as it should appear on the client\'s computer',
));
return $this->response;
}
The first parameter of $this->response->file is relative to your APP directory. So calling $this->response->file('someFolder' . DS . 'someFile.zip') will download the file APP/someFolder/someFile.zip.
“Sending files” requires at least CakePHP version 2.0. Please also consider taking a look at the Cookbook link above.
If you are running an older version of CakePHP you should use Media Views as you already mentioned in your question. Use this code and refer to Media Views (Cookbook).
Here's the same method for older versions:
public function download($id) {
$this->viewClass = 'Media';
$path = $this->YourModel->aMagicFunctionThatReturnsThePathToYourFile($id);
// in this example $path should hold the filename but a trailing slash
$params = array(
'id' => 'someFile.zip',
'name' => 'the name of the file as it should appear on the client\'s computer',
'download' => true,
'extension' => 'zip',
'path' => $path
);
$this->set($params);
}
Right way to generate download link in CakePHP 3
Place this function in AppController or Write a component and then call from other controllers.
Make sure to change the content-type as per the download file
public function downloadResponse() {
return $this->response
->withHeader('Content-Type', 'application/pdf')
->withHeader('Content-Disposition', 'attachment;')
->withHeader('Cache-Control', 'max-age=0')
->withHeader('Cache-Control', 'max-age=1')
->withHeader('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT')
->withHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' PDT')
->withHeader('Cache-Control', 'cache, must-revalidate')
->withHeader('Pragma', 'public')
->withFile($filePath, ['download' => true]);
}
Related
On my page I am making an invoice that is fully compatible with Livewire. I use this package: https://github.com/LaravelDaily/laravel-invoices to generate my invoice and everything works fine. But their is one problem I ran into. I can't download my PDF with Livewire.
Here is a basic example to generate a PDF and download it:
public function invoice()
{
$customer = new Buyer([
'name' => 'John Doe',
'custom_fields' => [
'email' => 'test#example.com',
],
]);
$item = (new InvoiceItem())->title('Service 1')->pricePerUnit(2);
$invoice = Invoice::make()
->buyer($customer)
->discountByPercent(10)
->taxRate(15)
->shipping(1.99)
->addItem($item);
return $invoice->download();
}
Whenever I click on a button
<a role="button" class="pdf-download cursor-pointer" wire:click="invoice">download</a>
Nothing happens. So the problem is that Livewire doesn't support this download method. And this download method looks like this:
public function download()
{
$this->render();
return new Response($this->output, Response::HTTP_OK, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'attachment; filename="' . $this->filename . '"',
'Content-Length' => strlen($this->output),
]);
}
$this->render(); Renders a template in a specific folder
Is their a work around for this? Where I can download my pdf with a template or maybe a different strategy. I allready tried one thing. I stored the invoice into a session, like so:
Session::put('invoice', $invoice);
Session::save();
And in a different controller I have.
if ($invoice = Session::get('invoice')) {
$invoice->download();
}
But that gives me this error:
serialization of 'closure' is not allowed
And I tried some stuff I found here: https://github.com/livewire/livewire/issues/483
But nothing works. Can someone give me a direction on where to look or how to fix this? Thanks!
return response()->streamDownload(function () use($invoice) {
echo $invoice->stream();
}, 'invoice.pdf');
Seems to do the trick.
I have a controller method like so:
use Symfony\Component\HttpFoundation\StreamedResponse;
public function downloadTransactions($type)
{
$this->authorize('download', Invoice::class);
$filename = 'invoices-' . strtolower($type) . '.csv';
$response = new StreamedResponse(function() {
Invoice::generateTransactionsCsv($type);
}, 200, [
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
]);
return $response;
}
and then in my Invoice model I am populating the file:
public static function generateTransactionsCsv($type = null)
{
// Open output stream
$handle = fopen('php://output', 'w');
// Add CSV headers
fputcsv($handle, [
'ID',
'REF',
'DESCRIPTION',
'DATE',
'AMOUNT',
]);
// Close the output stream
fclose($handle);
}
but I get an ERR_INVALID_RESPONSE in Chrome, although I would assume the browser doesn't matter. I've checked similar questions which suggest installing the zip extension but I already have it installed. Using PHP 7.1 locally. Also tried looking at the logs but there doesn't seem to be anything there (using Valet locally).
If I move the logic from the model to the controller then it works fine but my example above is simplified to just the header row of the csv, in reality, there's a bit more to it so I'd like to keep the logic in the model if possible.
I've also tried opening and closing the file handle in the controller and passing it the model but that didn't work either.
Looks like it was because the $type variable wasn't being passed correctly:
$response = new StreamedResponse(function() use ($type) {
....
}
Was able to figure it out thanks to Safari, it downloaded a csv with the Laravel error trace in it which was weird, I didn't think it would be any different from Chrome.
I'm getting a bit muddled with a CSV download. I'm very happy to save it to a file and supply a link to the user, but this seems like the wrong way to go judging from things like these.
Going from this answer Use Laravel to Download table as CSV I think I've found that the stream() method no longer exists.
public function download()
{
$headers = [
'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0'
, 'Content-type' => 'text/csv'
, 'Content-Disposition' => 'attachment; filename=galleries.csv'
, 'Expires' => '0'
, 'Pragma' => 'public'
];
$list = $this->users->getAllUsers()->toArray();
# add headers for each column in the CSV download
array_unshift($list, array_keys($list[0]));
$callback = function() use ($list)
{
$FH = fopen('php://output', 'w');
foreach ($list as $row) {
fputcsv($FH, $row);
}
fclose($FH);
};
// return Response::stream($callback, 200, $headers); // Old version
return response()->download($callback, 'Users-' . date('d-m-Y'), $headers);
}
I've tried to use the Laravel 5.2 response() function, however I'm just getting a bit lost as to what I'm responding with – download() seems the logical option, but that gives me the following error:
Object of class Closure could not be converted to string
Which makes sense. What is the right way of going about this? Or should I save the file and then just use the filepath as the first argument of my download() function – something that seems to be bad practise?
It was simple enough and worked great once I replaced the class & static call Response:: With the helper function, response()->:
return response()->stream($callback, 200, $headers);
I believe this uses the StreamedResponse class.
Here is my function to download the uploaded files:
public function download() {
$this->viewClass = 'Media';
$params = array(
'id' => 'article.pdf',
'name' => 'article',
'download' => true,
'extension' => 'pdf',
'path' => WWW_ROOT . DS . 'files' . DS
);
$this->set($params);
}
However calling this function results in the following error message:
Error: Media could not be found.
Error: Create the class Media below in file: src\View\Media.php
Any suggestions what is wrong with my code? Thanks in advance!
Any suggestions what is wrong with my code?
Yes:
Wrong CakePHP Version (3.x) for what you try to use
And even in 2.x Media view is deprecated since 2.3 as the documentation clearly states on top of the page: Deprecated since version 2.3: Use Sending files instead.
Read the documentation!
Taken from the link above:
public function sendFile($id)
{
$file = $this->Attachments->getFile($id);
$this->response->file($file['path']);
// Return response object to prevent controller from trying to render
// a view.
return $this->response;
}
And always put the exact CakePHP version you're using in your question.
Error: Create the class Media below in file: src\View\Media.php
Create file in
Path - src/View/
File Name - media.php
I'm using Laravel 4 framework, I have a function that creates a csv file called data_78888.csv the number 78888 changes everytime the function is run to generate a csv file. That function returns a string like that : "Download/78888"
The folder where my csv files are created is called "outputs" and is located in my project folder where the app folder is located to, (it is not in the public folder).
What I would like to do is to create a route that points to my Process controller like that :
Route::get('Download/{token}', array('uses' => 'ProcessController#downloadCSV'));
In my controller I would like to send that csv file to the browser to download it , I'm doing like that :
<?php
class ProcessController extends BaseController {
public function downloadCSV($token){
$fileToDownload = "data_".$token.".csv";
$filePath = "outputs/";
return Response::download($filePath, $fileToDownload, array(
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment;filename="'.$fileToDownload
));
}
}
The issue is that this is not working and I get an html file called 78888.htm and an error on the server.
How can I make this working please?
The path to the file has to include the name and file extension of the file.
So try this;
$fileToDownload = "data_".$token.".csv";
$filePath = base_path() . "outputs/" . $fileToDownload;
return Response::download($filePath, $fileToDownload, array(
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment;filename="'.$fileToDownload
));
Also make sure the file exists, before downloading.