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!
23.
Endpoint for Downloading Private Files
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
}
}
15 Comments
Hi! And thank you for this cast, it really helps!
I'm trying to make a "music on demand" service such as Spotify (a very very VERY small one ;-). Logged users can listening songs but I don't want them to download the original files. To to that, I tried to use the StreamResponse object as you did in the course:
`
$response = new StreamedResponse(function() use ($song, $uploaderHelper) {
});
`
This system is very cool because I can create a one-shot URL (with a token) as an endpoint to load the mp3 file in my page. And the real URL stays hidden.
But the download performances from s3 are very very VERY bad!
As I don't want users can download the original files, I think I can't use s3 presigned-URL... I really want to have a one-shot URL not one with expiration time.
Any idea?
Cyril (from France)
Hi Cyril S.!
Yes, the problem with "proxying" files of *any* significant size through a Symfony controller is that they will be *slow*. This is a great solution for things like PDF files... or something else. But videos or audio files, it's just not going to scale.
More generally, audio & video streaming are special "cases" - there are special protocols for doing this stuff - like RTMP or HLS https://en.wikipedia.org/wi... - and if you want a robust streaming services, you really need to do some research into this. I am definitely not an expert on it, so I can't say much unfortunately :).
But, if you are *generally* ok with streaming from S3 (or CloudFront in front of S3), then you will need to do signed URLs. However, you said this:
> This system is very cool because I can create a one-shot URL (with a token) as an endpoint to load the mp3 file in my page
You could *still* totally do this... but then the controller for this page would create a signed URL and redirect the user to that URL. So yes, the URL in the browser would ultimately be the signed URL to S3. But you would *still* be able to generate these one-shot URLs with a token and send them to people.
> As I don't want users can download the original files, I think I can't use s3 presigned-URL
If you want to avoid people downloading the files, then you *really* need to check into HLS or something like it - for example https://docs.aws.amazon.com... - streaming data to people... without actually allowing them to download the file is tricky business. Even with your current solution, I could use the one-shot URL to download the music file.
Cheers!
In fact, with my current solution, the one-shot URL is not given to the user as this. It is calculated by the controller just before loading the page and destroyed just after. The *visible* URL in the media player is so unusable for downloading. It works great except the speed...
Thanks for your answer. I will check into HLS!
edit template is incomplete :)
Hey Ad F.
Thank you for reporting this! We had a bug with Emoji char, now it's fixed and all code blocks should be good. If you notice any problems with code blocks, please, let us know.
Cheers!
Hi there !
When I use this :
<br />public function downloadArticleReference(ArticleReference $reference)<br />{<br />dd($reference);<br />}<br />I have the following error :
<br />Cannot autowire argument $reference of "App\Controller\ArticleReferenceAdminController::downloadArticleReference()": it references class "App\Entity\ArticleReference" but no such service exists.<br />To fix that, I have to use the following code instead :
`
`
So I have 2 questions :
Thanks for your help :)
Hey Stileex,
Unfortunately, ArticleReference and ArticleReferenceRepository are totally different things. We need to fetch the proper ArticleReference entity in that class. Well, you can fetch it from ArticleReferenceRepository manually, and then it will work well. But you can't just use ArticleReferenceRepository object as ArticleReference object in the code. If you see that error - most probably you misprint some namespaces in your projects. Make sure you use "Symfony\Component\Routing\Annotation\Route" in that controller, and make sure that "ArticleReference" class exists in src/Entity/ directory and has the same name and its namespace is "App\Entity". Also, do have "{id}" in your route path? It should be like this: '@Route("/admin/article/references/{id}/download", name="admin_article_download_reference", methods={"GET"})'
Btw, did you download the course code and started from start/ directory? Or did you started from scratch on a newer version of Symfony? Which version is it?
Cheers!
Hi again Victor :)
So first, this is the reply to your last question: I have started my project test from the latest version of Symfony. I am not really using the course code, even if I have downloaded it and I am using it to check my own code. I think it's a better way to do, otherwise I would just copy paste what it is shown in the video, it forces me to fully understand what I am doing.
I have check everything you told me: Entity, Namespace, Route, the ID parameter, everything is here.
Maybe let me ask you my question in another way.
In the course code, you have this:
` /**
{id} is an integer (or could be a string in general)
But in the function downloadArticleReference you get it, then use it with the variable $reference which is an ArticleReference object.
How come you can do this ?? Is it a shortcut given by Symfony ?
That's why in my code, I fetch the object ArticleReferenceRepository manually with a database query first (using {id}).
I think it is this:
https://symfony.com/doc/cur...
And I had this problem because this component was not installed...
sensio/framework-extra-bundle
Hey Stileex,
Ah, yes! That "sensio/framework-extra-bundle" package should be installed to be able to use entity type-hint feature. Good catch!
Cheers!
Hi !
I used some parts of this tutorial, including to upload some excel files.
The system of upload is working fine: in my particular case, the file is uploaded in <i>/var/uploads/import_user_file</i>
But I know need to use this excel file and I struggle for something:
In a controller or in a service, how can I retrieve the path to this file ?
I thought I could use the <i><b>function readStream()</b></i>
<b>And here in my controller:</b>
But... the dump return file not found, and of course, it looks like the proccess is not going to the folder /var/upload
Can somebody explain me, I think I miss something here.
Thanks!
Hey Caeema,
Well, it depends on what you need to do with that file. First of all, readStream() function returns you a resource. But if you need to get the content of that file - you probably may want to use file_get_contents() native PHP function to get the actual content of the file. But it might be memory consuming, working with resources might be more lightweight. But once again, it depends on what you're going to do with that excel file. If you have an Excel reader - a library you installed in your project - and that reader works with resources - probably it would be fine, just pass it the correct resource. But some readers may expect only path to the file, and they they will open it for reading/wringing themselves. It depends on the library API, etc.
But if you see that the file is not found - that probable mean that you path is incorrect. I'd recommend you to dump the path you specified in your controller and follow your filesystem manually to see that file there. Maybe it's incorrect and miss some folders in it, maybe you get doubled slash ("//") in it, maybe you forgot to pass an exceptions of the file, etc. So, first of all you need to make sure that the file really exists in the path you have in your controller.
I hope this helps!
Cheers!
2:42 I know, it is not frontend tutorial but for information: please don't forget about blind people and accessibility. Instead just icon, inside anchor put the whole text "Download %s", and replace %s with full name of the title.First: a11y screen readers are not able to read the icon. Second: "download" alone it's not fine, anchor text must be understandable outside of its context — a11y screen readers are able to generate list of all anchors of the website. If you want to hide the text for standard non-blind people, wrap it inside `span` but don't use display: none, use `sr-only` class instead of look for visually hidden technique if you don't use bootstrap in your project.
Hey TomaszGasior!
That's really great advice - and I should have done a better job on that - I don't mind being called out on something I could have done it better :). I will keep this in mind more on future tutorials - better accessibility is just a win for everything/everyone.
Cheers!
"Houston: no signs of life"
Start the conversation!