I am running Symfony 1.4 on Linux. My application creates pdf files and saves the files in the following directory:
/srv/www/vhosts/myapp/htdocs/stmts
Here is an example of the path to a particular pdf file:
/srv/www/vhosts/myapp/htdocs/stmts/example_001.pdf
My symfony is installed in the following path:
/srv/www/vhosts/myapp/htdocs
How can I create a route from my Symfony application to the example_001.pdf file? I want to be able to create a link in my symfony application to the pdf file. When a user clicks on the link the pdf will be opened.
Thank You
In order for using a route to make sense you would need to be doing something like this:
public function executeDownload(sfWebRequest $request)
{
// assume this method holds the logic for generating or getting a path to the pdf
$pdfPath = $this->getOrCreatePdf();
// disbale the layout
$this->setLayout(false);
$response = $this->getResponse();
// return the binary pdf dat directly int he response as if serving a static pdf file
$response->setHttpHeader('Content-Disposition', 'attachment; filename="'. basename($pdfPath));
$response->setContentType('application/pdf');
$response->setContent(file_get_contents($pdfPath));
return sfView::NONE;
}
That action would actually read the file and send the content. But unless you have a good reason for doing this its not advisable because youre going to incur unnecessary overhead from php.
If you do have a good reason for doin this (restricted access, dynamic file names, etc.) then you would simply determine what paramters you need to use in that action to determine the path to the pdf on the file system and set up a normal route. For example lets say your using a human recognizeable slug to refernece the file. Then you have a db record that holds a mapping of slug to file path. In that case the preceding action might look like this:
public function executeDownload(sfWebRequest $request)
{
$q = Doctrine_Core::getTable('PdfAsset')
->createQuery('p')
->where('slug = ?', $request->getSlug());
$this->forward404Unless($asset = $q->fetchOne());
$pdfPath = $asset->getPath();
// disbale the layout
$this->setLayout(false);
$response = $this->getResponse();
// return the binary pdf dat directly in the response as if serving a static pdf file
$response->setHttpHeader('Content-Disposition', 'attachment; filename="'. basename($pdfPath));
$response->setContentType('application/pdf');
$response->setContent(file_get_contents($pdfPath));
return sfView::NONE;
}
With the corresponding route looking something like:
pdf_asset:
url: /download/pdf/:slug
params: {module: yourModule, action: 'download'}
Note if the files are large you might want to use fopen instead of file_get_contents and then read the data out as a stream so that you dont have to put it all in memory. This would require you to use a view though (but you would still set layout to false to prevent the layout from wrapping your streamed data).
Related
What I want to archive:
I want to write a feature test using phpunit in Laravel.
What does the controller I want to test do:
It accepts uploads of test records to store it into a database. Each record consists of two files (xml, yml) with the same file name. Both files have to be read and stored in the database. The uploaded files are never stored on the server, they are directly processes.
What I want to test:
Upload a record and check if the correct data is in the database and available over the API
Check if I get the correct error if one file (xml or yml) is missing
Check if I get the right error if the files are not a valid record
and so on ...
What is my current problem?
I would like to use a template for the xml and yml files and use Faker to generate fake data for this test. The reason: Why not? My understanding of testing is, that you should test as many cases as possible and if static data is enough why do we use Faker and Factory in the Unit tests for the database and so on?
When I look at Laravel: Testing File Uploads, they generate there testing files with UploadedFile::fake(). My understanding of those files is, that they are empty and you can't use a template or something like that, to fill it with useful data. Most solutions I found just kept real files in their project. Like this.
I could use blade for this, as shown here, but I'm not really sure if I should abuse blade like this.
I could fully generate the xml and yml files using Yaml and XMLReader/XMLWriter, but there is a lot of static text in those files and I only need to fill data into some specific points.
Questions:
So what is the best way to create such a fake file? Should I use blade or twig or some other templating engine? A small solution would be appreciated.
Or should I generate the full file by myself and why is this better?
Or is there no point in generating fake data and I should use static data instead?
Here is my current test function:
public function testFullRecordUpload() {
// Generate record files to upload
// TODO Use template files with faker, or something like that
$xml_file = UploadedFile::fake()->create('test_file.xml', $sizeInKilobytes = 566);
$yml_file = UploadedFile::fake()->create('test_file.yml', $sizeInKilobytes = 20);
// Send Files
$response = $this->json('POST', '/upload', [
'xml' => $xml_file,
'yml' => $yml_file
]);
// Check response
$response->assertOk();
// Check if the uploaded data is available over API
// TODO
}
Goal: When client requests to see a file from server, i want the client's browser to ask what to do (open or download file). How can i build that?
So far i have methods like those:
public static function getFileContent($fullpath){
//yes, i can get both file and its extension
$file = File::get($fullpath);
$mimeType = File::extension($fullpath);
$response = Response::make($file, 200, array('content-type'=>$mimeType));
return $response;
}
and the other method calls the above
$path = $instance->the_file_path_on_the_database;
return MyClass::getFileContent($path);
In this case, if the uploaded file is PDF, png, jpeg etc. browser automatically opens the file, which is okay.
But when it comes to the *.xlsx or *.docx, browser asks me what to do but the file name isn't as the same as what i have stored on the database and it has no extension. Also the file is automatically renamed as the route's name.
Thanks in advance.
according to the docs
The download method may be used to generate a response that forces the
user's browser to download the file at the given path. The download
method accepts a file name as the second argument to the method, which
will determine the file name that is seen by the user downloading the
file. Finally, you may pass an array of HTTP headers as the third
argument to the method:
return response()->download($pathToFile);
return response()->download($pathToFile, $name, $headers);
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.
I'm recoding an existing project by using the CakePHP framework. Now, in my existing code I have a PHP file (just called thumb.php) which takes a file and the size and yields a resized raw image to the browser. This can then be utilized in for instance an image tag or for other purposes. This "thumbs.php" file just uses a header('Content-Type: image/jpeg'); to output a raw image.
Anyway, here it is in action:
http://www.dosspirit.net/php/thumb.php?file=268-transport-tycoon-deluxe-3.png&size=640
(You can change the size parameter to like 240 or even 1240 and the image will be resized for you).
Now, I want to replicate this behaviour in cakephp.
So far, I have this setup:
class ScreenshotController extends AppController {
public function view() {
(Some code to handle image scaling)
}
}
Looking at the above code, urls to a screenshot would be like this: http://www.domain/screenshot/view/268-transport-tycoon-deluxe-3.png/640
Now, how can I tell a controller in cakePHP to output the image header instead of going through a view etc., hence mimicing the functionality from the thumbs.php file ?
Any response is appreciated,
sincerely,
- bakkelun
There's still PHP in CakePHP. You can do the same thing in your action:
public function view() {
header('…');
echo $imageData;
exit();
}
Having said that, you should use the Controller::header method to output raw headers (for overrideable unit tests). Even better, you should use Media Views to output images the Cake Way (which requires you to write to a file though).
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.