Chapters
40 Chapters
|
4:39:58
|
Login to bookmark this video
-
Course Code
Subscribe to download the code!Compatible PHP versions: ^7.1.3
Subscribe to download the code!Compatible PHP versions: ^7.1.3
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Subtitles
Subscribe to download the subtitles!
Subscribe to download the subtitles!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
24.
Streaming the File Download
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
Subscribe to jump to this part in the video!
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
This tutorial is built on Symfony 4 but works great in Symfony 5!
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": "^7.1.3",
"ext-iconv": "*",
"aws/aws-sdk-php": "^3.87", // 3.87.10
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"doctrine/annotations": "^1.0", // 1.10.1
"doctrine/doctrine-bundle": "^1.6.10", // 1.10.2
"doctrine/doctrine-migrations-bundle": "^1.3|^2.0", // v2.0.0
"doctrine/orm": "^2.5.11", // v2.7.2
"knplabs/knp-markdown-bundle": "^1.7", // 1.7.1
"knplabs/knp-paginator-bundle": "^2.7", // v2.8.0
"knplabs/knp-time-bundle": "^1.8", // 1.9.0
"league/flysystem-aws-s3-v3": "^1.0", // 1.0.22
"league/flysystem-cached-adapter": "^1.0", // 1.0.9
"liip/imagine-bundle": "^2.1", // 2.1.0
"nexylan/slack-bundle": "^2.0,<2.2.0", // v2.1.0
"oneup/flysystem-bundle": "^3.0", // 3.0.3
"php-http/guzzle6-adapter": "^1.1", // v1.1.1
"phpdocumentor/reflection-docblock": "^3.0|^4.0", // 4.3.0
"sensio/framework-extra-bundle": "^5.1", // v5.2.4
"stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
"symfony/asset": "^4.0", // v4.2.3
"symfony/console": "^4.0", // v4.2.3
"symfony/flex": "^1.9", // v1.21.6
"symfony/form": "^4.0", // v4.2.3
"symfony/framework-bundle": "^4.0", // v4.2.3
"symfony/property-access": "4.2.*", // v4.2.3
"symfony/property-info": "4.2.*", // v4.2.3
"symfony/security-bundle": "^4.0", // v4.2.3
"symfony/serializer": "4.2.*", // v4.2.3
"symfony/twig-bundle": "^4.0", // v4.2.3
"symfony/validator": "^4.0", // v4.2.3
"symfony/web-server-bundle": "^4.0", // v4.2.3
"symfony/yaml": "^4.0", // v4.2.3
"twig/extensions": "^1.5" // v1.5.4
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.0", // 3.1.0
"easycorp/easy-log-handler": "^1.0.2", // v1.0.7
"fzaninotto/faker": "^1.7", // v1.8.0
"symfony/debug-bundle": "^3.3|^4.0", // v4.2.3
"symfony/dotenv": "^4.0", // v4.2.3
"symfony/maker-bundle": "^1.0", // v1.11.3
"symfony/monolog-bundle": "^3.0", // v3.3.1
"symfony/phpunit-bridge": "^3.3|^4.0", // v4.2.3
"symfony/stopwatch": "4.2.*", // v4.2.3
"symfony/var-dumper": "^3.3|^4.0", // v4.2.3
"symfony/web-profiler-bundle": "4.2.*" // v4.2.3
}
}
20 Comments
what do you do when a user from Japan uploads a weird file name
HeaderUtils::makeDisposition('attachment', 'ςЎβξЯиęł ŁĮωέ.docx')throws - The filename fallback must only contain ASCII characters.
Hey Ad F.
Thanks for the good question!
For such cases there is 3rd argument in
makeDispositionmethod. You should provide your own generated ASCII filename, for the fallback.Cheers!
Hello, my local server is running on windows with XAMPP, I am having a problem to stream the file download due to the incompatibility between the slashes in windows and unix systems. I have followed this tutorial and I find myself with this problem:
<blockquote>Unable to read file
from location: invoices/FACTURA-2022-1.pdf.
fopen(D:\www\rakatangaclean\var\uploads\invoices/FACTURA-2022-1.pdf):
Failed to open stream: No such file or directory</blockquote>
The error trace points at :
$fileStream = $uploadHelper->readStream($invoice->getFilePath(), false);My "invoices" entity's getFilePath property is defined the following way:
In uploadHelper.php I have defined the INVOICES folder this way:
const INVOICES = "invoices"At the oneup_flysystem.yaml
I find it difficult to overcome the slashes issue between windows and unix systems.
Hey Juan E.!
Hmmm. Ok, first question: does the path (ignoring possible "slash problems")
D:\www\rakatangaclean\var\uploads\invoices/FACTURA-2022-1.pdfactually exist, or not? If it doesn't, then we have some other problem. If it DOES, then yes, it seems like a "slash" problem... though I'm pretty sure that PHP is pretty good these days at just "figuring it out" when it comes to the slashes.So if the file DOES exist... and so we think that we have a "slash" problem, try this. In some controller somewhere, put this code:
That is the same path from the error... except that I have two
\\... just because that's what you need to do in a PHP string when you want a backslash. Anyways, in theory, that should return false, because this is the path that originally wasn't working. If it DOES return false, try "playing with the slashes" to see if you can get it to return true. For example, does changing/FACTURA-2022-1.pdfto<br />\\FACTURA-2022-1.pdfmake it return true? If so, then we can be 100% sure that we have a "slash problem".Let me know what you find out :).
Cheers!
Hello!
Is it possible to set the disposition to "inline" but in a new tab?
Thx!
Hey Lydie,
Yes, sure! Actually, there's nothing server side :) You just need to add target="_blank" for that link, it will be opened in a new tab then. I.e. it's a plain HTML feature, not PHP :)
Cheers!
Hi there, thanks for the great tutorial!
I was wondering, is there a reason why you don't add validation to stream_copy_to_stream? The documentation mentions that it also returns false on failure. We've been adding those validation to streams so far so it made me wonder.
Cheers!
Hey julien_bonnier!
Excellent question! There was no reason for this - just an oversight on my part, to be honest. For an abundance of caution, adding a check would probably make sense :).
Cheers!
I am having weird header already sent error while generating the CSV and exporting for download on the fly. The header already sent error is at the line:
$response = new StreamedResponse(function () use ($output) {
Hey Ghazanfar,
Hm, it sounds like you hit some kinda of limit, maybe memory limit, maybe something else, not sure. Do you see a Symfony error? Try to check the entire stack trace, it may contain some tips.
Also, first of all, make sure that your data structure is valid... you may hit some edge case where data is not valid, i.e. maybe you need one more if statement for handle that edge case.
If nothing helps - I'd recommend you to debug things more thoroughly. E.g. use logger and log data for every iteration right in the beginning, *before* you will handle those data. Then, run again and when it failed with the error you mentioned - look at logs and see on what data it failed. You can also exclude those data and see if no errors. If you always have error on the same iteration - most probably you hit some kind of limit in your PHP configuration.
I hope this helps!
Cheers!
Hi guys :)
I've just few question about the way to serve the file, why do not use the file shortcut method, or directly use the BinaryFileResponse ?
return $this->file('invoice_3241.pdf', 'my_invoice.pdf', ResponseHeaderBag::DISPOSITION_INLINE);It return a stream. (see: https://symfony.com/doc/current/controller.html#streaming-file-responses)
Or in my case I prefer the BinaryFileResponse to use
X-SendFile(https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/) and do not serve at all the file with PHP, but just return a header that say to Nginx: "Hey, give him this file please." Then, this method can also stream the File by doing anew Sream('path/of/the/file');then give it to the BinaryFileResponse. (see: https://symfony.com/doc/current/components/http_foundation.html#serving-files)Hey VERMON!
Excellent questions!
In this case, we didn't use the
file()method (which creates aBinaryFileResponseas you know) simply because we are not working with a local file on our filesystem. Instead, we're working with Flysystem, and the file could be stored locally... or could be up on S3. The only thing we know is that we're able to get a "stream" - and so we use the StreamedResponse instead. If I knew that I had a local file, I'd totally use BinaryFileResponse - it's much easier :).You're right - this is a faster way than serving files through PHP. But again, you need to know for sure that you're working with local files in the filesystem - and we don't have that guarantee in this situation because we're using Flysystem. In reality, in a real app, if I were "streaming" larger files, because download performance of these files would be so important, I would "avoid" using Flysystem in this one situation and instead, either (A) stream the files from the filesystem using X-SendFile or (B) use signed URLs with S3/CloudFront so that the user can securely download the files directly from S3/CloudFront instead of proxying through our system (which will definitely slow things down).
Cheers!
Hi! How can I extract the file from a given path? I make things more clear, I have some private files which are accessible only to some user categories. So I have the var->upload->category1 category2 etc. I need to extract all the files in one of this directory in order to show them the files who belogs to that category. How can I do that?
Hey Gballocc7
Are you going to read those files from S3 or from your local file system? Anyway, when working with big files the best thing you can do is to open a stream socket like Ryan demonstrates in this episode. And for reading a directory, if you are using the same library as on the tutorial (
league/flysystem) then I believe the method you are looking for islistContents. Check out its docs: https://flysystem.thephpleague.com/docs/usage/filesystem-apiI hope this helps. Cheers!
Hi
I'm building a new system which includes file uploads. I'm trying to do the first part of the tutorial (images) but need them to be private yet viewable in the system. Using public images means they are accessible via the url irrespective of the system being secured.
I've got the images and thumbs in the /var directory but can't show them.
I've added a private_uploads_base_url in services.yaml, edited liip_imagine.yaml so the filesystem_service uses oneup_flysystem.private_uploads_filesystem_filesystem. I've added a getPrivatePath() in the UploaderHelper, new twig function for the asset but I can't seem to output the image.
Any tips on where I'm going wrong or how I can use the second part of the tutorial (private files) but adapt it to have a view of the image and not a download?
Many thanks
Steve
Hey Steve D.
If you already store private files and implemented the endpoint for being able to download them, then it should be very easy to instead of returning a file just return the image content. This guide might help you out: https://www.quora.com/How-d...
Basically you just need to set the proper headers and return the image content as your response. In your frontend you just have to call such endpoint inside a HTML img element
Cheers!
A great help thank you Diego very much appreciated, I'm now successfully returning the image. However, it is the original image and very large. I'm trying (so far without any luck) to filter this image using the Liipimagine bundle and pass the filtered image back.
Stop the press. Since I began writing this I've actually managed to sort it. Taken me a good few hours to sort but happy now.
If anyone reads this and needs help let me know.
Here, @Steve-D 🖐️
I have quite the same problem. Returning the Image as a stream (instead of forcing a download) works, but handing it over to liip and using the thumbnails / filtered images does not.
It would be great if you could share your solution.
If you share your code I might be able to help... Rather than me share mine which might not be relevant, if I see yours I could offer up where mine is similar
Steve
Hi Thanathan
This goes back a few years and I can't quite recall how I fixed it in the end, lots of trial and error and throwing every possible option at it until something worked. The application has changed a lot since this and I don't think I would use this method again. If/when this app gets rebuilt I'll probably use AWS as there is a lot of images and wouldn't load them like this. Due to the qty of images loading it's very slow.
Sorry I can't help any further and sorry for not posting the solution at the time.
Steve
"Houston: no signs of life"
Start the conversation!