Stream a PDF file in Laravel 5.2 - php

Hi everyone! I'm working on a Laravel 5.2 application in which the user can download a variety of files. One of these is a 'User guide', explaining how the website is set up and functionalities etc. I would like the PDF to be streamed in another page, so that the user is still within the application. The controller which I am using is:
public function userguidePDF(){
return response()->stream('../public/download/userguide.pdf');
}
But this returns:
Argument 1 passed to
Symfony\Component\HttpFoundation\StreamedResponse::__construct() must
be callable, string given, called in
/path/to/laravel/vendor/laravel/framework/src/Illuminate/Routing/ResponseFactory.php
on line 117 and defined
I have searched on the internet for a method, which leaded me to the following syntax:
return response()->stream($callback, 200, $headers);
Unfortunately, I am unable to find more documentation on the parameters because I don't understand them. Could someone please explain to me what the $callback, 200, $header are as parameters and how I can use this?

https://laravel.com/docs/5.2/responses#view-responses
public function userguidePDF() {
return response()->file(
public_path('download/userguide.pdf')
);
}

$callback should be a function that ouputs your PDF. For example:
$callback = function()
{
$handle = fopen("../public/download/userguide.pdf", "r");
$filecontent= fread($handle, filesize("../public/download/userguide.pdf"));
fclose($handle);
return $filecontent;
};

You can use download also
return Response::download($file, 'filename.pdf', $headers);
here is the documentation for streaming and also downlaod.
https://laravel.com/api/5.2/Illuminate/Routing/ResponseFactory.html#method_stream
EDIT
Use headers like this
// We'll be outputting a PDF
header('Content-Type: application/pdf');
// It will be called downloaded.pdf
header('Content-Disposition: attachment; filename="downloaded.pdf"');

Related

Print html before downloading zip file

Hello fellow coders !
I am looking for a solution to show a html page while my php code prepares a .zip which is then downloaded. The reason is because sometimes the zips are quite bigger and take time to make.
The HTML page would be a basic "Please wait while your .zip files is being prepared".
The PHP side used is Symfony. So I come into my getInboxExportAction function by calling https://myapi.com/orders/orderid/inbox/export.
The download function (makeExportDownloadRequestResponse) works fine. But if I add a flush after making my $response, the .zip is printed to the html, instead of being downloaded...
public function getInboxExportAction(Request $request, $orderId)
{
$response = new Response($this->twig->render('base.html.twig',
['content' => '
<h1>Download</h1>
<p>Your zip is being prepared for download, please wait...</p>
']));
$response->headers->set('Content-Type', 'text/html');
//Here I would like to echo + flush my html.
//Then stop the flush and continue my code
$receivedOrder = $this->fetchRequestedReceivedOrder($orderId);
if (!$receivedOrder instanceof ReceivedOrder) {
return $receivedOrder;
}
if (!$receivedOrder->getSentorder()) {
$this->makeErrorResponse('Sent order was not found', Response::HTTP_BAD_REQUEST);
}
return $this->makeExportDownloadRequestResponse($receivedOrder->getSentorder(), $request->get('format'));
}
I am also very open to any other ideas anyone would have to fix this issue :)
Thanks,
Edit : My $this->makeExportDownloadRequestResponse() function returns a response, not a path. We unlink the file on the server for storage reasons.
$content = file_get_contents($zipTmpFile);
unlink($zipTmpFile);
$response = new Response($content);
$dispositionHeader = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT,
'myFile_' . date('Y_m_d_h_i_s') . '.zip');
$response->headers->set('Content-Disposition', $dispositionHeader);
return $response;
Second edit : I understand that what I'm trying to do (change the content-type within one call) I generally frowned upon. I'm currently trying to think of a more elegant solution.
In your getInboxExportAction, just return a response with your text.
Then, in this response, add a <meta http-equiv="refresh" content="1;url=xxx"> tag to redirect the user to another action that will generate the zip file.
You can also handle this redirection with javascript.
Note that you can use a StreamedResponse to handle the zip download: https://symfony.com/doc/current/components/http_foundation.html#streaming-a-response
The most important thing is you set $response->headers->set('Content-Type', 'text/html');, the browser will not download the .zip file.
If your makeExportDownloadRequestResponse method can return the absolute .zip file path (or you can calculate it), you can try:
public function getInboxExportAction(Request $request, $orderId)
{
$response = new Response($this->twig->render('base.html.twig',
['content' => '
<h1>Download</h1>
<p>Your zip is being prepared for download, please wait...</p>
']));
$response->headers->set('Content-Type', 'text/html');
//Here I would like to echo + flush my html.
//Then stop the flush and continue my code
$receivedOrder = $this->fetchRequestedReceivedOrder($orderId);
if (!$receivedOrder instanceof ReceivedOrder) {
return $receivedOrder;
}
if (!$receivedOrder->getSentorder()) {
$this->makeErrorResponse('Sent order was not found', Response::HTTP_BAD_REQUEST);
}
// prepare the to be downloaded zip file
// then, do sth to get the .zip file path
$zipFilePath = $this->makeExportDownloadRequestResponse($receivedOrder->getSentorder(), $request->get('format'));
// re-set header
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename=whateverName.zip');
header('Content-Length: ' . filesize($zipFilePath));
readfile($zipFilePath);
}
It will let browser downloads the zip file.
Finally, If your function is work as an API, and you need a frontend JS to execute it, try with Blob class on JS.
You can indeed use a StreamedResponse to get it rid of this issue. About the fact that the whole .wip file content being displayed because of the flush, try to call ob_flush before

Laravel File Downloaded from storage folder gets corrupted

I tried to download jpg/png image from storage folder its gets corrupted after downloaded.
This is my controller
public function download($filename) {
$headers = array(
'Content-Type: image/png',
);
return response()->download(storage_path() . '/'.$filename, 'final.png', $headers);
}
after open it look like this
Even i used core php script to download still iam facing same problem.
I believe that the Laravel framework might be introducing whitespace which might be ruining the header() function.
Use ob_end_clean() before your first header() call to remove any extra whitespace.
add this method before like this
ob_end_clean();
$headers = array(
'Content-Type: image/png',
);
return response()->download(storage_path() . '/'.$filename, 'final.png', $headers);
!enjoy
After Download .jpg,png,gif and .xlsx
Yes ! Its working;
I was reading this problem for 15 days and finally got the solution
Larabel 5.8 & PHP 7.3
public function download_user_photo($id) {
ob_end_clean();
$photo = UserGallery::find($id);
$path = 'storage/users/'.$photo->image_url;
enter code here
if (\File::exists($path)) {
return response()->download($path);
} else {
return redirect()->back()->with('danger', 'Image does not exist.');
}
}

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

Generating a PDF using CodeIgniter

I am using dompdf to create a pdf file out of an html file that gets created on-the-fly for the sole purpose of it serving as input for the pdf generator, however I am having trouble doing this, I implemented the code in this thread and everything works fine (I could output a simple pdf) however when I try to give it a more specific url I get this error:
An Error Was Encountered Unable to
load the requested file
here's the code that has the problem:
function printPDF(){
//write_file() usa un helper (file)
$this->load->library('table');
$this->load->plugin('to_pdf');
// page info here, db calls, etc.
$query = $this->db->get('producto');
$data['table'] = $this->table->generate($query);
$path_url = base_url().'print/existencias.html';
write_file($path_url, $data['table']);
$html = $this->load->view($path_url, 'consulta', true);
pdf_create($html, 'consulta');
}
Not sure about the exact problem, but please check this:
1) as stated in CI's manual, load->view's second parameter should be an associative array or an objet, translated to vars via extract. That may generate some problem generating $html.
2) try making $path_url relative to application/views directory, as read in CI's manual.
you should use tcpdf to create a pdf.
//create controller for example :
public function create_pdf()
{
$this->load->library("Pdf");
$data['results'] = // your data
$this->load->view('pdfview',$data);
}
//pdfview is the page having tcpdf code and your pdf code.
You can try it like this
public function export_pdf() {
$filename = 'FILENAME.pdf';
header("Content-Description: Advertise Report");
header("Content-Disposition: attachment; filename=$filename");
header("Content-Type: application/pdf; ");
$file = fopen('php://output', 'w');
$header = array("Question", "Answer", "Name", "Email", "Phone", "Date");
fputpdf($file, $header);
fputpdf($file, 'YOUR DATA');
fclose($file);
exit;
}

Downloading a blob (type: image) from a mssql db in zend framework

How do I do it? I've tried to use file_put_contents(), but it created a corrupted file.
I'm now trying to set up a view with the correct headers to get this working.
<?php
header('Content-Type: image/jpeg');
header('Content-Transfer-Encoding: base64');
echo $this->dokument->Dokument;
this is the content of my controller:
public function imageAction()
{
$id = $this->_getParam('id');
$dokumentTabell = new Ordre_Model_Table_OrdreDokument();
$dokument = $dokumentTabell->find($id)->current();
$this->view->dokument = $dokument;
// disable layout and view
$this->view->layout()->disableLayout();
}
After editing it according to the first answer here, it gives this error message: PHP Fatal error: Call to a member function setHttpResponseCode() on a non-object in ...\application\modules\ordre\controllers\OrdreController.php on line 45
line 45 is this:
->setHttpResponseCode(200)
The rest of the code:
public function imageAction()
{
$id = $this->_getParam('id');
$dokumentTabell = new Ordre_Model_Table_OrdreDokument();
$dokument = $dokumentTabell->find($id)->current();
$this->view->dokument = $dokument;
$filename = $dokument->Name.".".$dokument->FileExtension;
// disable layout and view
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
$this->getResponse()
->clearAllHeaders()
->clearBody()
->setHttpResponseCode(200)
->setHeader('Content-Type', 'image/jpeg')
->setHeader('Content-Length', strlen($dokument->Dokument))
->setHeader(
'Content-Disposition',
"attachment;filename=\"{$filename}\""
)
->setHeader('Last-Modified', $dokument->SistEndret)
->setBody($dokument->Dokument);
}
If I remove line 44: ->clearBody(), the code "works". I get a file with the right name and approximately the right size.
In my opinion, views should never output headers.. To me, it feels much more like a controller thing to do. Also, in the controller you have access to the response object, which has methods for setting headers.
Also, is $dokument->Dokument base64-encoded? Otherwise, just skip that header.
I'd probably do something like:
public function imageAction()
{
$id = $this->_getParam('id');
$dokumentTabell = new Ordre_Model_Table_OrdreDokument();
$dokument = $dokumentTabell->find($id)->current();
$this->view->dokument = $dokument;
// disable layout and view
$this->_helper->Layout()->disableLayout();
$this->_helper->ViewRenderer->setNeverRender();
$this->getResponse()
->clearAllHeaders()
->clearBody()
->setHttpResponseCode(200)
->setHeader('Content-Type', 'image/jpeg')
->setHeader('Content-Length', strlen($dokument->Dokument))
->setHeader(
'Content-Disposition',
"attachment;filename=\"{$dokument->filename}\""
)
->setHeader('Last-Modified', $dokument->lastModified)
->setBody($dokument->Dokument);
}

Categories