Symfony - Custom API - Force download [duplicate] - php

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

Related

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

Laravel - display a PDF file in storage without forcing download?

I have a PDF file stored in app/storage/, and I want authenticated users to be able to view this file. I know that I can make them download it using
return Response::download($path, $filename, $headers);
but I was wondering if there is a way to make them view the file directly in the browser, for example when they are using Google Chrome with the built-in PDF viewer. Any help will be appreciated!
Update for 2017
As of Laravel 5.2 documented under Other response types you can now use the file helper to display a file in the user's browser.
return response()->file($pathToFile);
return response()->file($pathToFile, $headers);
Source/thanks to below answer
Outdated answer from 2014
You just need to send the contents of the file to the browser and tell it the content type rather than tell the browser to download it.
$filename = 'test.pdf';
$path = storage_path($filename);
return Response::make(file_get_contents($path), 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'inline; filename="'.$filename.'"'
]);
If you use Response::download it automatically sets the Content-Disposition to attachment which causes the browser to download it. See this question for the differences between Content-Disposition inline and attachment.
Edit: As per the request in the comments, I should point out that you'd need to use Response at the beginning of your file in order to use the Facade.
use Response;
Or the fully qualified namespace if Response isn't aliased to Illuminate's Response Facade.
Since Laravel 5.2 you can use File Responses
Basically you can call it like this:
return response()->file($pathToFile);
and it will display files as PDF and images inline in the browser.
In Laravel 5.5 you can just pass "inline" as the disposition parameter of the download function:
return response()->download('/path/to/file.pdf', 'example.pdf', [], 'inline');
Ben Swinburne's answer is absolutely correct - he deserves the points! For me though the answer left be dangling a bit in Laravel 5.1 which made me research — and in 5.2 (which inspired this answer) there's a a new way to do it quickly.
Note: This answer contains hints to support UTF-8 filenames, but it is recommended to take cross platform support into consideration !
In Laravel 5.2 you can now do this:
$pathToFile = '/documents/filename.pdf'; // or txt etc.
// when the file name (display name) is decided by the name in storage,
// remember to make sure your server can store your file name characters in the first place (!)
// then encode to respect RFC 6266 on output through content-disposition
$fileNameFromStorage = rawurlencode(basename($pathToFile));
// otherwise, if the file in storage has a hashed file name (recommended)
// and the display name comes from your DB and will tend to be UTF-8
// encode to respect RFC 6266 on output through content-disposition
$fileNameFromDatabase = rawurlencode('пожалуйста.pdf');
// Storage facade path is relative to the root directory
// Defined as "storage/app" in your configuration by default
// Remember to import Illuminate\Support\Facades\Storage
return response()->file(storage_path($pathToFile), [
'Content-Disposition' => str_replace('%name', $fileNameFromDatabase, "inline; filename=\"%name\"; filename*=utf-8''%name"),
'Content-Type' => Storage::getMimeType($pathToFile), // e.g. 'application/pdf', 'text/plain' etc.
]);
And in Laravel 5.1 you can add above method response()->file() as a fallback through a Service Provider with a Response Macro in the boot method (make sure to register it using its namespace in config/app.php if you make it a class). Boot method content:
// Be aware that I excluded the Storage::exists() and / or try{}catch(){}
$factory->macro('file', function ($pathToFile, array $userHeaders = []) use ($factory) {
// Storage facade path is relative to the root directory
// Defined as "storage/app" in your configuration by default
// Remember to import Illuminate\Support\Facades\Storage
$storagePath = str_ireplace('app/', '', $pathToFile); // 'app/' may change if different in your configuration
$fileContents = Storage::get($storagePath);
$fileMimeType = Storage::getMimeType($storagePath); // e.g. 'application/pdf', 'text/plain' etc.
$fileNameFromStorage = basename($pathToFile); // strips the path and returns filename with extension
$headers = array_merge([
'Content-Disposition' => str_replace('%name', $fileNameFromStorage, "inline; filename=\"%name\"; filename*=utf-8''%name"),
'Content-Length' => strlen($fileContents), // mb_strlen() in some cases?
'Content-Type' => $fileMimeType,
], $userHeaders);
return $factory->make($fileContents, 200, $headers);
});
Some of you don't like Laravel Facades or Helper Methods but that choice is yours. This should give you pointers if Ben Swinburne's answer doesn't work for you.
Opinionated note: You shouldn't store files in a DB. Nonetheless, this answer will only work if you remove the Storage facade parts, taking in the contents instead of the path as the first parameter as with the #BenSwinburne answer.
I am using Laravel 5.4 and response()->file('path/to/file.ext') to open e.g. a pdf in inline-mode in browsers. This works quite well, but when a user wants to save the file, the save-dialog suggests the last part of the url as filename.
I already tried adding a headers-array like mentioned in the Laravel-docs, but this doesn't seem to override the header set by the file()-method:
return response()->file('path/to/file.ext', [
'Content-Disposition' => 'inline; filename="'. $fileNameFromDb .'"'
]);
As of laravel 5.5 if the file is stored on a remote storage
return Storage::response($path_to_file);
or if it's locally stored you can also use
return response()->file($path_to_file);
I would recommend using the Storage facade.
Laravel 5.6.*
$name = 'file.jpg';
store on image or pdf
$file->storeAs('public/', $name );
download image or pdf
return response()->download($name);
view image or pdf
return response()->file($name);
Retrieve File name first then in Blade file use anchor(a) tag like below shown. This would works for image view also.
view Pdf ;
Ben Swinburne answer was so helpful.
The code below is for those who have their PDF file in database like me.
$pdf = DB::table('exportfiles')->select('pdf')->where('user_id', $user_id)->first();
return Response::make(base64_decode( $pdf->pdf), 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'inline; filename="'.$filename.'"',
]);
Where $pdf->pdf is the file column in database.
Retrieving Files
$contents = Storage::get('file.jpg');
Downloading Files
return Storage::download('file.jpg');
File URLs
$url = Storage::url('file.jpg');
You can try this, it will open a pdf or other asset as a tab in your browser:
Link to you doc
Using the anchor tag means it behaves link any other link.

KnpSnappyBundle Save File To Server

I am missing something in the KnpSnappy Bundle docs. :( How do I save my pdf to the server using Symfony and KnpSnappy. I don't want my pdf to download to the browser. I want it to save to the server. Please help! Thanks so much.
Server Path: $pdfFolder = $_SERVER['DOCUMENT_ROOT'].'/symfonydev/app/Resources/account_assets/'.$account_id.'/pdf/';
return new Response(
$this->get('knp_snappy.pdf')->getOutputFromHtml($content),
200,
array(
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'attachment; filename="'.$pdfFolder.''.strtotime('now').'.pdf"'
)
);
As you can see from documentation you need to use another function:
$this->get('knp_snappy.pdf')->generateFromHtml($content, $pdfFolder . time() . '.pdf';
As you can see I replaced your strtotime('now') by time(). It will be faster.
This line generates the PDF...
$this->get('knp_snappy.pdf')->getOutputFromHtml($content)
So you could probably just use file_put_contents to write the result of getOutputFromHtml($content) to the file system.

Force-Download with php on Amazon S3

I am trying to use http://code.google.com/p/amazon-s3-php-class/ to force-dowload files from AWS S3. I have an mp3 that I want people to "play" or "download." By default the when you access the file directly on s3 it begins to play in the browser. I need to add an option to actually download. I have Googled and found came up with nothing. I conceptually know what needs to happen but don't know how to produce it php. I know I need to modify the headers to Content-Disposition: attachment. Any help would be greatly appreciated.
Thanks,
Michael
Amazon has now solved this problem and allows overriding of headers on a per-request basis with signed requests:
http://docs.amazonwebservices.com/AmazonS3/latest/API/index.html?RESTObjectGET.html
w00t!
The php scripts that have been mentioned so far will work ok, but the main downside is that every time a visitor on your site requests a file, your own servers will load it from the S3 and then relay that data to the browser. For low traffic sites, it's probably not a big deal, but for high traffic ones, you definitely want to avoid running everything through your own servers.
Luckily, there's a fairly straight-forward way to set your files to be forced to download from the S3. And you're exactly right - you just want to set the content-type and content-disposition (just setting content-disposition will work in some browsers, but setting both should work in all browsers).
This code is assuming that you're using the Amazon S3 PHP class from Undesigned:
<?php
// set S3 auth and get your bucket listing
// then loop through the bucket and copy each file over itself, replacing the "request headers":
S3::copyObject($bucketName, $filename, $bucketName, $filename, "public-read", array(), array("Content-Type" => "application/octet-stream", "Content-Disposition" => "attachment"));
?>
Now all your files will be forced to download. You may need to clear your cache to see the change. And obviously, don't do that on any file that you actually do want to be loaded "inline" in the browser.
The nice part with this solution is that applications that load media files directly (like let's say an mp3 player in Flash) don't care about the content-type or content-disposition, so you can still play your files in the browser and then link to download that same file. If the user already finished loading the file in flash, they'll most likely still have it in their cache, which means their download will be super quick and it won't even cost you any extra bandwidth charges from the S3.
Just add this to your file's metadata on s3:
Content-Disposition: attachment; filename=FILENAME.EXT
Content-Type: application/octet-stream
Just wanting to post a contribution to this, Alex Neth is correct on this reference, but i do not feel a link is sufficient information, using amazon's own AWS PHP SDK2. Below I've outlined a basic (untested) method for calling data this way, you can use either S3 Factory Method or AWS Service Builder to make the S3 Client.
<?php
// S3 Factory Method
/*use Aws\S3\S3Client;
$s3= S3Client::factory(array(
'key' => '<aws access key>',
'secret' => '<aws secret key>'
));*/
// OR AWS Service Builder
use Aws\Common\Aws;
// Create a service builder using a configuration file
$aws = Aws::factory('/path/to/my_config.json');
// Get the client from the builder by namespace
$s3 = $aws->get('S3');
// Now lets create our request object.
$command = $s3->getCommand('GetObject',array(
'Bucket' => 'your-bucket-name',
'Key' => 'keyname',
'ResponseContentType' => 'application/octet-stream',
'ResponseContentDisposition' => 'attachment; filename="filename.mp3',
));
$url = $command->createPresignedUrl('+1 days');
?>
You can then use PHP's header("Location: $url"); in order to redirect the visitor to the MP3 file with a force download, this should prevent it from playing in the browser, Please note, i use ResponseContentType quite frequently but I've never used ResponseContentDisposition with AWS (it should work according to the docs).
Converting this sample into a function should be easy, you could even pass in $bucket, $key, $force_download as such
<?php
use Aws\Common\Aws;
function gen_url($bucket,$key,$force_download=false){
// OR AWS Service Builder (personal method to do this)
// Create a service builder using a configuration file
$aws = Aws::factory('/path/to/my_config.json');
// Get the client from the builder by namespace
$s3 = $aws->get('S3');
$params = array(
'Bucket' => $bucket,
'Key' => 'keyname',
'ResponseContentType' => 'application/octet-stream',
'ResponseContentDisposition' => 'attachment; filename="filename.mp3',
);
if($force_download){
$params['ResponseContentType'] = 'application/octet-stream';
$params['ResponseContentDisposition'] = 'attachment; filename="'.basename($key).'"';
}
$command = $s3->getCommand('GetObject',$params);
return $command->createPresignedUrl('+1 days');
}
// Location redirection to an MP3 force downlaod
header("Location: ".gen_url("recordings","my-file.mp3",true));
// Location redirection to a MP3 that lets the browser decide what to do.
header("Location: ".gen_url("recordings","my-file.mp3"));
?>
WARNING, if you haven't figured it out, this requires the AWS PHP SDK 2 currently (April 7th 2014) found here http://aws.amazon.com/sdkforphp/ This code is mostly pseudo code and may require some additional tweaking to actually make work as i'm referencing this from memory.
So modify my example above to be like this
<?php
header('Content-Type: audio/mpeg');
header("Content-Disposition: attachment; filename={$_GET['file']};");
readfile("url to the file/{$_GET['file']}");
exit();
?>
Now you will want to put some validation in there so that you not giving the world access to every file you put on S3, but this should work.
If you are using a library like Tarzan AWS, you can add meta headers, that amazon will include when the file is retrieved. Check out the meta parameter in the update_object function here, for example:
http://tarzan-aws.com/docs/2.0/files/s3-class-php.html#AmazonS3.update_object
Also worth mentioning is you are able to hard-set the headers for files in S3. For example, if you need to force-download a certain file you can set the appropriate headers for that file. Such as defaulting to stream, and either having a secondary file set to force-download or spend the bandwidth to use php/fputs and force-download via PHP.
<?php
require_once __DIR__.'/vendor/autoload.php';
use Aws\Common\Aws;
function gen_url($bucket,$key,$force_download=false){
// OR AWS Service Builder (personal method to do this)
$config = array(
'key' => '',
'secret' => '',
);
// Create a service builder using a configuration file
$aws = Aws::factory($config);
// Get the client from the builder by namespace
$s3 = $aws->get('S3');
$params = array(
'Bucket' => $bucket,
'Key' => $key,
'ResponseContentType' => 'application/octet-stream',
'ResponseContentDisposition' => 'attachment; filename="'.$key,
);
if($force_download){
$params['ResponseContentType'] = 'application/octet-stream';
$params['ResponseContentDisposition'] = 'attachment; filename="'.basename($key).'"';
}
$command = $s3->getCommand('GetObject',$params);
return $command->createPresignedUrl('+1 days');
}
$bucket = '';
$filename = '';
$url = gen_url($bucket,$filename,true);
echo "\n".$url."\n\n";
The code above works, you just need to install the S3 composer dependency and link in the autoload file, put your key/secret into config and then supply the bucket/filename.
php would just download the file to the server, not the client. remember, php doesn't do anything on the client, it just returns content (html, javascript, css, xml, whatever...)
[edit: added for clarity]: php can serve audio content, but you want to serve a web page and audio at the same time. To get that client behaviour, you have to get the client to request the file based on the web page's html or javascript.
So you have to get the client to download the file. For instance, have an iframe on the page with the url of the file on s3. Use css to make the iframe invisible. It should render the page and download and play the mp3.
Otherwise, look into using javascript to kick of a download when the page loads. I'm not sure if that's possible.
This is now possible by overwriting the S3 headers using signed requests.
Request Parameters
There are times when you want to override certain response header values in a GET response. For example, you might override the Content-Disposition response header value in your GET request.
You can override values for a set of response headers using the query parameters listed in the following table.
response-content-type - Sets the Content-Type header of the response
response-content-disposition - Sets the Content-Disposition header of the response.
Note
You must sign the request, either using an Authorization header or a pre-signed URL, when using these parameters. They cannot be used with an unsigned (anonymous) request.
So, you would set those headers to:
response-content-disposition: attachment; filename=FILENAME.EXT
response-content-type: application/octet-stream
Found answer here: https://stackoverflow.com/a/5903710/922522
<?php
// PHP solution (for OP and others), works with public and private files
// Download url expires after 30 minutes (no experation after the download initiates, for large files)
// ***Forces client download***
$signed_url = $s3_client->getObjectUrl($s3_bucket_name, $s3_filename_key, '+30 minutes', array(
'ResponseContentType' => 'application/octet-stream',
'ResponseContentDisposition' => 'attachment; filename="your-file-name-here.mp4"'
));
redirect($signed_url);
I never tried Amazon's S3 hosting, but don't you have access to using .htaccess files there? Then you can set Content-Type and Content-Disposition for an entire directory with this entry:
<IfModule mod_headers.c>
<FilesMatch "\.(mp3)$">
ForceType audio/mpeg
Header set Content-Disposition attachment
</FilesMatch>
</IfModule>

Forcing browser to show pdf file instead of downloading it in laravel 4 [duplicate]

I have a PDF file stored in app/storage/, and I want authenticated users to be able to view this file. I know that I can make them download it using
return Response::download($path, $filename, $headers);
but I was wondering if there is a way to make them view the file directly in the browser, for example when they are using Google Chrome with the built-in PDF viewer. Any help will be appreciated!
Update for 2017
As of Laravel 5.2 documented under Other response types you can now use the file helper to display a file in the user's browser.
return response()->file($pathToFile);
return response()->file($pathToFile, $headers);
Source/thanks to below answer
Outdated answer from 2014
You just need to send the contents of the file to the browser and tell it the content type rather than tell the browser to download it.
$filename = 'test.pdf';
$path = storage_path($filename);
return Response::make(file_get_contents($path), 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'inline; filename="'.$filename.'"'
]);
If you use Response::download it automatically sets the Content-Disposition to attachment which causes the browser to download it. See this question for the differences between Content-Disposition inline and attachment.
Edit: As per the request in the comments, I should point out that you'd need to use Response at the beginning of your file in order to use the Facade.
use Response;
Or the fully qualified namespace if Response isn't aliased to Illuminate's Response Facade.
Since Laravel 5.2 you can use File Responses
Basically you can call it like this:
return response()->file($pathToFile);
and it will display files as PDF and images inline in the browser.
In Laravel 5.5 you can just pass "inline" as the disposition parameter of the download function:
return response()->download('/path/to/file.pdf', 'example.pdf', [], 'inline');
Ben Swinburne's answer is absolutely correct - he deserves the points! For me though the answer left be dangling a bit in Laravel 5.1 which made me research — and in 5.2 (which inspired this answer) there's a a new way to do it quickly.
Note: This answer contains hints to support UTF-8 filenames, but it is recommended to take cross platform support into consideration !
In Laravel 5.2 you can now do this:
$pathToFile = '/documents/filename.pdf'; // or txt etc.
// when the file name (display name) is decided by the name in storage,
// remember to make sure your server can store your file name characters in the first place (!)
// then encode to respect RFC 6266 on output through content-disposition
$fileNameFromStorage = rawurlencode(basename($pathToFile));
// otherwise, if the file in storage has a hashed file name (recommended)
// and the display name comes from your DB and will tend to be UTF-8
// encode to respect RFC 6266 on output through content-disposition
$fileNameFromDatabase = rawurlencode('пожалуйста.pdf');
// Storage facade path is relative to the root directory
// Defined as "storage/app" in your configuration by default
// Remember to import Illuminate\Support\Facades\Storage
return response()->file(storage_path($pathToFile), [
'Content-Disposition' => str_replace('%name', $fileNameFromDatabase, "inline; filename=\"%name\"; filename*=utf-8''%name"),
'Content-Type' => Storage::getMimeType($pathToFile), // e.g. 'application/pdf', 'text/plain' etc.
]);
And in Laravel 5.1 you can add above method response()->file() as a fallback through a Service Provider with a Response Macro in the boot method (make sure to register it using its namespace in config/app.php if you make it a class). Boot method content:
// Be aware that I excluded the Storage::exists() and / or try{}catch(){}
$factory->macro('file', function ($pathToFile, array $userHeaders = []) use ($factory) {
// Storage facade path is relative to the root directory
// Defined as "storage/app" in your configuration by default
// Remember to import Illuminate\Support\Facades\Storage
$storagePath = str_ireplace('app/', '', $pathToFile); // 'app/' may change if different in your configuration
$fileContents = Storage::get($storagePath);
$fileMimeType = Storage::getMimeType($storagePath); // e.g. 'application/pdf', 'text/plain' etc.
$fileNameFromStorage = basename($pathToFile); // strips the path and returns filename with extension
$headers = array_merge([
'Content-Disposition' => str_replace('%name', $fileNameFromStorage, "inline; filename=\"%name\"; filename*=utf-8''%name"),
'Content-Length' => strlen($fileContents), // mb_strlen() in some cases?
'Content-Type' => $fileMimeType,
], $userHeaders);
return $factory->make($fileContents, 200, $headers);
});
Some of you don't like Laravel Facades or Helper Methods but that choice is yours. This should give you pointers if Ben Swinburne's answer doesn't work for you.
Opinionated note: You shouldn't store files in a DB. Nonetheless, this answer will only work if you remove the Storage facade parts, taking in the contents instead of the path as the first parameter as with the #BenSwinburne answer.
I am using Laravel 5.4 and response()->file('path/to/file.ext') to open e.g. a pdf in inline-mode in browsers. This works quite well, but when a user wants to save the file, the save-dialog suggests the last part of the url as filename.
I already tried adding a headers-array like mentioned in the Laravel-docs, but this doesn't seem to override the header set by the file()-method:
return response()->file('path/to/file.ext', [
'Content-Disposition' => 'inline; filename="'. $fileNameFromDb .'"'
]);
As of laravel 5.5 if the file is stored on a remote storage
return Storage::response($path_to_file);
or if it's locally stored you can also use
return response()->file($path_to_file);
I would recommend using the Storage facade.
Laravel 5.6.*
$name = 'file.jpg';
store on image or pdf
$file->storeAs('public/', $name );
download image or pdf
return response()->download($name);
view image or pdf
return response()->file($name);
Retrieve File name first then in Blade file use anchor(a) tag like below shown. This would works for image view also.
view Pdf ;
Ben Swinburne answer was so helpful.
The code below is for those who have their PDF file in database like me.
$pdf = DB::table('exportfiles')->select('pdf')->where('user_id', $user_id)->first();
return Response::make(base64_decode( $pdf->pdf), 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'inline; filename="'.$filename.'"',
]);
Where $pdf->pdf is the file column in database.
Retrieving Files
$contents = Storage::get('file.jpg');
Downloading Files
return Storage::download('file.jpg');
File URLs
$url = Storage::url('file.jpg');
You can try this, it will open a pdf or other asset as a tab in your browser:
Link to you doc
Using the anchor tag means it behaves link any other link.

Categories