Symfony2 - Force file download - php

I'm trying to download a file when a user clicks on download link.
In Controller:
$response = new Response();
$response->headers->set('Content-type', 'application/octect-stream');
$response->headers->set('Content-Disposition', sprintf('attachment; filename="%s"', $filename));
$response->headers->set('Content-Length', filesize($filename));
return $response;
This is opening the dialog box to save the file, but it says the file is 0 bytes.
And changing it to:
$response = new Response();
$response->headers->set('Content-type', 'application/octect-stream');
$response->headers->set('Content-Disposition', sprintf('attachment; filename="%s"', $filename));
$response->headers->set('Content-Length', filesize($filename));
$response->headers->set('Content-Transfer-Encoding', 'binary');
$response->setContent(readfile($filename));
return $response;
I get a bunch of weird characters instead of the file download dialog box.
Finally, switching the "setContent" line to:
$response->setContent(file_get_contents($filename));
It returns a PHP error:
Fatal error: Allowed memory size...
Any clues on how to achieve this?
I've done it before in PHP (wihtout MVC), but I don't know what can be missing to do it through Symfony2...
Maybe the solution is setting the memory_limit in PHP.INI, but I guess it´s not the best practice...

The most comfortable solution is
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
$response = new BinaryFileResponse($file);
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT);
return $response;

I finally solved this without X-SendFile (which is probably the best practice). Anyway, for those who can't get X-Sendfile apache module to work (shared hosting), here's a solution:
// Generate response
$response = new Response();
// Set headers
$response->headers->set('Cache-Control', 'private');
$response->headers->set('Content-type', mime_content_type($filename));
$response->headers->set('Content-Disposition', 'attachment; filename="' . basename($filename) . '";');
$response->headers->set('Content-length', filesize($filename));
// Send headers before outputting anything
$response->sendHeaders();
$response->setContent(file_get_contents($filename));
return $response;

You shouldn't use PHP for downloading files because it's a task for an Apache or Nginx server. Best option is to use X-Accel-Redirect (in case of Nginx) / X-Sendfile (in case of Apache) headers for file downloading.
Following action snippet can be used with configured Nginx to download files from Symfony2:
return new Response('', 200, array('X-Accel-Redirect' => $filename));
UPD1: Code for apache with configured mod_xsendfile:
return new Response('', 200, array(
'X-Sendfile' => $filename,
'Content-type' => 'application/octet-stream',
'Content-Disposition' => sprintf('attachment; filename="%s"', $filename))
);

As of Symfony 3.2 you can use the file() controller helper which is a shortcut for creating a BinaryFileResponse as mentioned in a previous answer:
public function fileAction()
{
// send the file contents and force the browser to download it
return $this->file('/path/to/some_file.pdf');
}

Don't know if it can help but it's application/octet-stream not application/octect-stream

+1 for alexander response.
But if you can't use X-Sendfile, you should use the BinaryFileResponse added in the 2.2: http://symfony.com/doc/current/components/http_foundation/introduction.html#serving-files
In my project the result is
$response = new \Symfony\Component\HttpFoundation\BinaryFileResponse($dir .DIRECTORY_SEPARATOR. $zipName);
$d = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$zipName
);
$response->headers->set('Content-Disposition', $d);
return $response;

For those who don't have the option of setting headers:
The download attribute may help depending on which browsers you need to support:
<a href="{file url}" download>
or
<a href="{file url}" download="{a different file name}">
This is not supported in all legacy browsers. See this page for browser support:
https://www.w3schools.com/tags/att_a_download.asp

Related

Excel exported as garbled PK zip file to browser

This is driving me crazy. I'm using a PHP function to download an Excel file. This works fine in my local environment and testing server, but on the production server, instead of downloading the spreadsheet, it prints a lot of garbled text to the browser window. It looks like the "PK" code for a zip file. The result is inconsistent. I reduce the number of columns and it works. Then I added one more column and it breaks. Then the next day it works. Then I add another column and it breaks. The same function works in other areas of the app, it's just when I try to export this one file.
This is a Symfony 4.4 app running on PHP 8.1. All environments should be identical.
$filePath = $path . $fileName;
$response = new Response();
$response->headers->set('Content-Type', "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
$response->headers->set('Content-Disposition', "attachment;filename={$fileName}");
$response->headers->set('Cache-Control', "no-cache");
$response->headers->set('Expires', "0");
$response->headers->set('Content-Transfer-Encoding', "binary");
$response->headers->set('Content-Length', filesize($filePath));
$request = Request::createFromGlobals();
$response->prepare($request);
$response->setContent(readfile($filePath));
$response->sendContent();
unlink($filePath);
return $response;
I got it. Instead of trying to set all those headers manually I just used the ResponseHeaderBag. Works now.
return $this->file("export/".$fileName, $fileName, ResponseHeaderBag::DISPOSITION_INLINE);
Right off the bat, readfile() does not return the contents of the file, but rather outputs it directly so:
$response->setContent(readfile($filePath));
is definitely wrong.
Instead use file_get_contents:
$response->setContent(file_get_contents($filePath));
I would refactor the code to read the file contents and set the Content-Length according to how many bytes $fileContents is:
$fileContents = file_get_contents($filePath);
$response->headers->set('Content-Length', strlen($fileContents));
$request = Request::createFromGlobals();
$response->prepare($request);
$response->setContent($fileContents);
(strlen() always returns the number of bytes in a string, not characters).

Return an in memory file in Symfony

I'm trying to return a BinaryFileResponse response in a Symfony Controller but it asks for a path or a File object. The thing is that I'm getting the file over a SOAP service, and it creates an object like this: filename + base64 encoded content + mime + extra stuff.
I don't really want to save it to disk then create the response...
Maybe I'm blinded at the moment, but is there any way to send it without creating a file in disk?
This is the action:
public function downloadAction($hash){
$document = $this->get('soap_services.document_service')->findDocument($hash);
return $this->file($SymfonyFileObjectOrPath, $fileName);
}
The $document object offers the following relevant methods: getMime, getName, getContent.
That's what I want to use to make a response without creating a File object (and the physical file that it implies).
Hello maybe you should consider to just give a Response and force the download "manually".
// Generate response
$response = new Response();
$filename = 'yourFileName.txt';
// Set headers
$response->headers->set('Cache-Control', 'private');
$response->headers->set('Content-type', $yourMimeType );
$response->headers->set('Content-Disposition', 'attachment; filename="' . $filename . '";');
$response->headers->set('Content-length', strlen($yourContentString));
// Send headers before outputting anything
$response->sendHeaders();
$response->setContent( $yourContentString );
return $response;
Maybe something like this will do the trick.
Can you try this, maybe change it a little bit?

symfony - download files

I am trying download files in controller but in the console I see only really bunch of weird characters instead of download box. I am following this topic Symfony2 - Force file download.
Don't know what is going on... trying to find simplest solution.
Here is my code:
$response = new Response(file_get_contents($file->realPath), 200, array(
'Content-Type' => $file->mimeType,
'Content-Length' => filesize($file->realPath),
'Content-Disposition' => 'attachment; filename=' . $file->name,
));
$response->send();
I've even tried to use the most basic example with header() and readfile().
Does my server need special config or something? Cheers.
Instead of rebuilding that kind of response, you could use Symfony's built-inBinaryFileResponse.
use Symfony\Component\HttpFoundation\BinaryFileResponse;
$response = new BinaryFileResponse($file);
Please take also a look in the documentation about serving files.
In the controller you can use $this->file(...)
The file needs full filesystem path with file to download
return $this->file('/home/website/upload/'.$someFile)
Also it is possible to define another name when downloading:
return $this->file('/home/website/upload/'.$someFile, 'MyFile.pdf');

symfony2 redirect to stream file

Hi guys I have a little question in symfony2.
Can you tell me how to redirect in controller to open (stream) a file in asset folder
Not to force to download
for example :
I have a file in root/web/bundles/ace/file.pdf
I used this code, but this failed to open /stream the file. This code force browser to download
$path = $this->get('kernel')->getRootDir().DIRECTORY_SEPARATOR."..".DIRECTORY_SEPARATOR."web".DIRECTORY_SEPARATOR."bundles".DIRECTORY_SEPARATOR.
"bsibkpm".DIRECTORY_SEPARATOR."download".DIRECTORY_SEPARATOR . $fileName;
//echo $path;die();
$content = file_get_contents($path);
$response = new Response();
$response->headers->set('Content-Type', 'application/pdf');
$response->headers->set('Content-Disposition', 'attachment;filename="'.$fileName);
$response->sendHeaders();
$response->setContent($content);
return $response;``
Many thanks for your help before
Hendrawan
You should set Content-Disposition to inline
$response->headers->set('Content-Disposition', 'inline;filename="'.$fileName);
P.S. check this bundle https://github.com/igorw/IgorwFileServeBundle

Creating a temp .htm file in PHP and send it to the user?

Is there a simple way to create an empty temp .htm file on the server and send it to the user as download? I don't need to really create the file with fopen(), it's enough to just create it temporarily and send it to the user.
I need that for a website authentication. I found there is tmpfile() function within PHP, but it doesn't quite work for me yet. I am working on a Symfony project, maybe there are also a header function I don't know of.
All I need is:
$token = '12345';
$filename = $token . '.htm';
createatmpfile($filename);
header(send .htm file as download to the user);
Not sure how that works, but maybe my pseudocode above explains what I am looking for ..
EDITED:
That's how it works for Symfony2 (thanks Grad):
use Symfony\Component\HttpFoundation\Response;
Controller method:
public function generateAction() {
$response = new Response();
$response->headers->set('Content-Disposition', 'attachment; filename="'.$token.'.htm"');
return $response;
}
In plain PHP you could use to following line to force the user to download the response
header('Content-Disposition: attachment; filename="filename.htm"');
In Symfony you can use:
$this->getResponse()->setHttpHeader('Content-Disposition', 'attachment; filename="filename.htm"');
With this header set you can either just execute your controller (and let the view fill in the response), or use $contents = file_get_contents($path) together with return $this->renderText($contents).
But depending on what you're trying to achieve: you don't have to create a tempfile to force the to download it.

Categories