If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeConfig done! Let's get to work in UploaderHelper
. Instead of passing the $uploadsPath
, which we were using to store things, change this to FilesystemInterface
- the one from Flysystem
- $filesystem
. Use that below, and rename the property to $filesystem
.
Tip
If you're using version 4 of oneup/flysystem-bundle
(so, flysystem
v2),
autowire Filesystem
instead of FilesystemInterface
from League\Flysystem
.
... lines 1 - 5 | |
use League\Flysystem\FilesystemInterface; | |
... lines 7 - 10 | |
class UploaderHelper | |
{ | |
... lines 13 - 14 | |
private $filesystem; | |
... lines 16 - 18 | |
public function __construct(FilesystemInterface $filesystem, RequestStackContext $requestStackContext) | |
{ | |
$this->filesystem = $filesystem; | |
$this->requestStackContext = $requestStackContext; | |
} | |
... lines 24 - 47 | |
} |
Now, in the method, instead of $file->move()
, we can say $this->filesystem->write()
, which is used to create new files. Pass this self::ARTICLE_IMAGE.'/'.$newFilename
and then the contents of the file: file_get_contents()
with $file->getPathname()
.
... lines 1 - 10 | |
class UploaderHelper | |
{ | |
... lines 13 - 24 | |
public function uploadArticleImage(File $file): string | |
{ | |
... lines 27 - 33 | |
$this->filesystem->write( | |
self::ARTICLE_IMAGE.'/'.$newFilename, | |
file_get_contents($file->getPathname()) | |
); | |
... lines 38 - 39 | |
} | |
... lines 41 - 47 | |
} |
That's it! This File
object has a ton of different methods for getting the filename, the full path, the file without the extension and more. Honestly, I get them all confused and have to Google them. getPathname()
gives us the absolute file path on the filesystem.
Above, we can get rid of the unused $destination
variable. Because the filesystem's root is public/uploads/
, the only thing we need to pass to write()
is the path relative to that: article_image/
and then $newFilename
.
I think we're ready! Let's clear out the uploads/
directory again. And then try our fixtures:
php bin/console doctrine:fixtures:load
Oh! It does not work!
Unused binding
$uploadsPath
in serviceUniqueUserValidator
.
This is a bad error message from Symfony, at least the second half of the message. A minute ago, we had an argument here called $uploadsPath
. Open up config/services.yaml
. Ah, that worked because we have $uploadsPath
configured as a global bind. And when you configure a bind, it must be used in at least one place in your app. If it's not used anywhere, you get this error. It's kinda nice: Symfony is saying:
Hey! You configured this bind... but you're not using it - are you maybe... messing something up on accident?
The UniqueUserValidator
part of the message is really a bug in the error message, which makes this a bit confusing.
Anyways, remove that bind and try the fixtures again:
php bin/console doctrine:fixtures:load
This is the error I was waiting for.
Cannot autowire service
UploaderHelper
argument$filesystem
of__construct()
referencesFilesystemInterface
but no such service exists.
There are two ways to fix this. First, we could re-add the alias
option and point it at this FilesystemInterface
:
# config/packages/oneup_flysystem.yaml
oneup_flysystem:
# ...
filesystems:
public_uploads_filesystem:
adapter: public_uploads_adapter
alias: League\Flysystem\Filesystem
Or, we can create a new bind. I'll do the second, because it works better if you have multiple filesystem services, which we will soon. First, rename the argument to be more descriptive, how about $publicUploadFilesystem
:
... lines 1 - 10 | |
class UploaderHelper | |
{ | |
... lines 13 - 18 | |
public function __construct(FilesystemInterface $publicUploadsFilesystem, RequestStackContext $requestStackContext) | |
{ | |
$this->filesystem = $publicUploadsFilesystem; | |
... line 22 | |
} | |
... lines 24 - 47 | |
} |
Then, under bind, set $publicUploadFilesystem
to the filesystem service id - you can see it in the error. It suggests two services that implement the FilesystemInterface
type-hint - we want the second one. Type @
then paste.
... lines 1 - 9 | |
services: | |
... lines 11 - 19 | |
bind: | |
... lines 21 - 22 | |
$publicUploadsFilesystem: '@oneup_flysystem.public_uploads_filesystem_filesystem' | |
... lines 24 - 48 |
One more time for the fixtures!
php bin/console doctrine:fixtures:load
Ok, no error! Check out the public/uploads/
directory. Yes! We have files! Refresh the homepage. We are good! We still need to tweak a few more details, but our app is now way more ready to work locally or in the cloud.
Hey ahmedbhs
Of course it's possible =) Check out previous chapter it was configured there! https://symfonycasts.com/sc...
Cheers!
Hey there ,
I'm using "oneup/flysystem-bundle": "^4.0",
and as Stephane mentioned, they removed the FilesystemInterface, and Diego suggested that we use League\Flysystem\FilesystemOperator
but i got a few errors, first:
Cannot autowire service "App\Service\FileStorage": argument "$filesystemOperator" of method "__construct()" references interface "League\Flysystem\FilesystemOperator" but no such service exists. You sh <br /> ould maybe alias this interface to one of these existing services: "oneup_flysystem.mount_manager", "oneup_flysystem.public_uploads_filesystem_filesystem".
This happens when i've commented alias: League\Flysystem\Filesystem
and with or without the bind $publicUploadsFilesystem: '@oneup_flysystem.public_uploads_filesystem_filesystem'
But if i remove the bind, and uncomment the alias, it works.
Hey Petru L.
As I see you try to bind $publicUploadsFilesystem: '@oneup_flysystem.public_uploads_filesystem_filesystem'
but in your App\Service\FileStorage
you are using FilesystemOperator $filesystemOperator
?
You should use FilesystemOperator $publicUploadsFilesystem
in your App\Service\FileStorage::__constructor()
to get bind work correctly
Cheers!
Hi Team,
I try to use "oneup/flysystem-bundle": "^4.0" to manage upload image.
I can't find FilesystemInterface in this version. There is "FilesystemAdapter" with method write. But this method has 3 parameters. The last is $config. What I have to use for this parameter ? I find nothing about that on bundle doc.
Cheers.
Hey Stephane
Yea, you're right. They dropped that interface in V2, I'm not aware of all the changes from V1 to V2 but it seems like you can rely on this other interface https://github.com/thephple...
although, some methods changed their signature, so if you find something not working as expected you may want to check this guide https://flysystem.thephplea...
and perhaps the CHANGELOG of the repository
Cheers!
Hey MolloKhan,
Thank you for your reply.
I succeeded to use the oneup/flysystem-bundle with FilesystemOperator interface.
When I use the command symfony console debug:container flysystem, I don't have "oneup_flysystem.public_uploads_filesystem_filesystem".
In the list there are 23 results with "oneup_flysystem.public_uploads_adapter_adapter" but not implement FilesystemInterface
There is "oneup_flysystem.default_filesystem_filesystem" and I use it and it's works until chapter 17.
I use {{ photo.imagePath|imagine_filter('image_thumb') }} after I config the loaders and data_loader into liip_imagine.yaml file and no thumbnail is created.
When I {{ dump(photo.imagePath) }} I have "trick_image/dscn2936-5d2df5309722a-60410d4c2fc3a.jpg".
The value of src of img markup is : https://localhost:8000/media/cache/resolve/image_thumb/trick_image/dscn2936-5d2df5309722a-60410d4c2fc3a.jpg
I don't see any solution. If you have some advice to check something.
Cheers.
What version of the LiipImagine bundle are you using? They added support to Flysystem V2 not to long ago. Check this pull request https://github.com/liip/Lii...
and here's an issue about it https://github.com/liip/Lii...
Thank for these links.
I use LiipImagine bundle version 2.5.0
For you, it is better to use https://github.com/thephple... that https://github.com/1up-lab/... ?
Hey @Stéphane!
Hmm. The phpleague one didn’t exist when I created this tutorial, but I think it will become / is the official solution (though both are great) and the phpleague one is built by a friend of mine that I trust.
In both cases, you’re still working with Flysystem under the hood. The bundles are a glue config layer. So you can almost just read the docs of each and choose the one with the config you like better :).
Cheers!
uploadArticleImage -> i got an error when i try to upload a image because is waitng UploadedFile, if i change the method with the UploadedFile no error but the image is not in the folder.
my method in uploadHelper:
public function uploadArticleImage(UploadedFile $file): string
{
$originalFileName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
$newFilename = $originalFileName . '-' . uniqid() . '.' . $file->guessExtension();
$stream = fopen($file->getPathname(), 'r');
$this->filesystem->writeStream(
self::ARTICLE_IMAGE.'/'.$newFilename,
$stream
);
if (is_resource($stream)) {
fclose($stream);
}
return $newFilename;
}
the method in my controller:
/**
* @Route("/admin/editArticle/{id}", name="edit_article")
*/
public function editArticle(Request $request, Article $article, UploaderHelper $uploaderHelper){
$form = $this->createForm(ArticleFromType::class, $article);
$form->handleRequest($request);
if ( $form->isSubmitted() && $form->isValid()) {
$article = $form->getData();
/**
* @var UploadedFile $uploadedFile
*/
$uploadedFile = $form['imageFile']->getData();
if ($uploadedFile){
$newFileName = $uploaderHelper->uploadArticleImage($uploadedFile);
$article->setImageFileName($newFileName);
}
$this->em->persist($article);
$this->em->flush();
return $this->redirectToRoute('list_article');
}
return $this->render('admin/articles/addArticle.html.twig', [
'articleForm' => $form->createView(),
]);
}
thanks!
Thank you guys for the great work as usual awesome idea and a wealth of learning. I was just wondering how do we fix auto-wiring issue using the first method i.e.
we could re-add the alias option and point it at this FilesystemInterface.
Hey Abdul,
We show this in the video, I updated the script and added the missing code block in case you only read scripts:
https://github.com/knpunive...
We also talk about aliases in the previous video: https://symfonycasts.com/sc... - you may want to check it.
Cheers!
Hi,
great tutorial but I get the following error while binding : `$publicUploadsFilesystem` parameter to '@oneup_flysystem.public_uploads_filesystem_filesystem'` service:
The service "App\Service\UploaderHelper"has a dependency on a non-existent service "oneup_flysystem.public_uploads_filesystem_filesystem"
I finally solve it moving the oneup config block into the main`services.yaml` file:
# Read the documentation: https://github.com/1up-lab/...
oneup_flysystem:
adapters:
public_uploads_adapter:
local:
directory: '%kernel.project_dir%/public/uploads'
filesystems:
public_uploads_filesystem:
adapter: public_uploads_adapter
Any idea, why ? Is this a an issue with the processing of separate configuration file with sf4 and oneup bundle?
Cheers,
ben
Hey Benoît M.
Hm, that's strange. Have you tried to debug it with php bin/console debug:container
and search there oneup_flysystem.public_uploads_filesystem_filesystem
service? Probably bundle is not configured properly, maybe a typo in config or somewhere else. Also you can use php bin/console debug:config <bundleName>
to see if it's configured as needed
Cheers!
Hi @Vladimir Sadicov,
no typo: I am cutting/pasting the following block from `config/packages/oneup_flysystem.yaml` to the end of `config/services.yaml`:
# Read the documentation: https://github.com/1up-lab/...
oneup_flysystem:
adapters:
public_uploads_adapter:
local:
directory: '%kernel.project_dir%/public/uploads'
filesystems:
public_uploads_filesystem:
adapter: public_uploads_adapter
When the block is set in `config/services.yaml`, I can run any command line like:
php bin/console debug:container public_uploads_filesystem
=>
Information for Service "oneup_flysystem.public_uploads_filesystem_filesystem"
==============================================================================
---------------- -------------------------------------------------------------
Option Value
---------------- -------------------------------------------------------------
Service ID oneup_flysystem.public_uploads_filesystem_filesystem
Class League\Flysystem\Filesystem
Tags oneup_flysystem.filesystem (key: public_uploads_filesystem)
Public yes
Synthetic no
Lazy no
Shared yes
Abstract no
Autowired no
Autoconfigured no
---------------- -------------------------------------------------------------
The `php bin/console debug:config oneup_flysystem` returns:
Current configuration for extension with alias "oneup_flysystem"
================================================================
oneup_flysystem:
adapters:
default_adapter:
local:
directory: /var/www/app/var/cache/dev/flysystem
lazy: false
writeFlags: 2
linkHandling: 2
permissions:
file:
public: 420
private: 384
dir:
public: 493
private: 448
public_uploads_adapter:
local:
directory: /var/www/app/public/uploads
lazy: false
writeFlags: 2
linkHandling: 2
permissions:
file:
public: 420
private: 384
dir:
public: 493
private: 448
filesystems:
default_filesystem:
adapter: default_adapter
alias: League\Flysystem\Filesystem
disable_asserts: false
plugins: { }
cache: null
mount: null
public_uploads_filesystem:
adapter: public_uploads_adapter
disable_asserts: false
plugins: { }
cache: null
alias: null
mount: null
cache: { }
cheers,
ben
Nice can you try same commands with default config place inside config/packages/oneup_flysystem.yaml
, but remove var binding to not produce an error, also better check will be php bin/console debug:container oneup_flysystem
to see all registered services
Cheers!
done, here is the output :
php bin/console debug:container oneup_flysystem
Select one of the following services to display its information:
[0 ] oneup_flysystem.adapter_factory.local
[1 ] oneup_flysystem.adapter_factory.nulladapter
[2 ] oneup_flysystem.adapter_fac...
[3 ] oneup_flysystem.adapter_factory.async_aws_s3
[4 ] oneup_flysystem.adapter_factory.awss3v2
[5 ] oneup_flysystem.adapter_factory.awss3v3
[6 ] oneup_flysystem.adapter_factory.azureblob
[7 ] oneup_flysystem.adapter_factory.dropbox
[8 ] oneup_flysystem.adapter_factory.googlecloudstorage
[9 ] oneup_flysystem.adapter_factory.rackspace
[10] oneup_flysystem.adapter_factory.webdav
[11] oneup_flysystem.adapter_factory.ftp
[12] oneup_flysystem.adapter_factory.sftp
[13] oneup_flysystem.adapter_factory.gridfs
[14] oneup_flysystem.adapter_factory.customadapter
[15] oneup_flysystem.adapter_factory.memory
[16] oneup_flysystem.adapter_factory.fallback
[17] oneup_flysystem.adapter_factory.gaufrette
[18] oneup_flysystem.adapter_factory.replicate
[19] oneup_flysystem.cache_factory.adapter
[20] oneup_flysystem.cache_factory.memory
[21] oneup_flysystem.cache_factory.noop
[22] oneup_flysystem.cache_factory.memcached
[23] oneup_flysystem.cache_factory.php_redis
[24] oneup_flysystem.cache_factory.predis
[25] oneup_flysystem.cache_factory.stash
[26] oneup_flysystem.cache_factory.psr6
[27] oneup_flysystem.adapter.local
[28] oneup_flysystem.adapter.cached
[29] oneup_flysystem.adapter.nulladapter
[30] oneup_flysystem.adapter.memory
[31] oneup_flysystem.adapter.zip
[32] oneup_flysystem.adapter.async_aws_s3
[33] oneup_flysystem.adapter.awss3v2
[34] oneup_flysystem.adapter.awss3v3
[35] oneup_flysystem.adapter.azureblob
[36] oneup_flysystem.adapter.dropbox
[37] oneup_flysystem.adapter.googlecloudstorage
[38] oneup_flysystem.adapter.rackspace
[39] oneup_flysystem.adapter.webdav
[40] oneup_flysystem.adapter.ftp
[41] oneup_flysystem.adapter.sftp
[42] oneup_flysystem.adapter.gridfs
[43] oneup_flysystem.adapter.fallback
[44] oneup_flysystem.adapter.gaufrette
[45] oneup_flysystem.adapter.replicate
[46] oneup_flysystem.mount_manager
[47] oneup_flysystem.filesystem
[48] oneup_flysystem.cache.adapter
[49] oneup_flysystem.cache.memory
[50] oneup_flysystem.cache.noop
[51] oneup_flysystem.cache.memcached
[52] oneup_flysystem.cache.predis
[53] oneup_flysystem.cache.php_redis
[54] oneup_flysystem.cache.stash
[55] oneup_flysystem.cache.psr6
[56] oneup_flysystem.plugin.empty_dir
[57] oneup_flysystem.plugin.get_with_metadata
[58] oneup_flysystem.plugin.list_files
[59] oneup_flysystem.plugin.list_paths
[60] oneup_flysystem.plugin.list_with
[61] oneup_flysystem.default_adapter_adapter
[62] oneup_flysystem.default_filesystem_filesystem
for sure:
php bin/console debug:config oneup_flysystem
Current configuration for extension with alias "oneup_flysystem"
================================================================
oneup_flysystem:
adapters:
default_adapter:
local:
directory: /var/www/app/var/cache/dev/flysystem
lazy: false
writeFlags: 2
linkHandling: 2
permissions:
file:
public: 420
private: 384
dir:
public: 493
private: 448
filesystems:
default_filesystem:
adapter: default_adapter
alias: League\Flysystem\Filesystem
disable_asserts: false
plugins: { }
cache: null
mount: null
cache: { }
Bingo, now we see that your config file is not loaded at all and that's weird. Let's check configs now it should be like this:config/packages/oneup_flysystem.yaml
``
oneup_flysystem:
adapters:
public_uploads_adapter:
local:
directory: '%kernel.project_dir%/public/uploads'
filesystems:
public_uploads_filesystem:
adapter: public_uploads_adapter
check the indentation and `dev/` folders another tip is try to clear cache but just delete cache folders not via console command
Hey best team !
when i load fixtures i got this error: File already exists at path: article_image/lightspeed
i removed the uploads directory and also dropped the database but nothing changed,
`
<?php
namespace App\Service;
use Gedmo\Sluggable\Util\Urlizer;
use League\Flysystem\FilesystemInterface;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class UploaderHelper
{
const ARTICLE_IMAGES = 'article_image';
private $filesystem;
public function __construct(FilesystemInterface $publicUploadsFilesystem)
{
$this->filesystem = $publicUploadsFilesystem;
}
public function uploadArticleImage(File $file): string
{
if($file instanceof UploadedFile){
$getOriginalName = $file->getClientOriginalName();
}else{
$getOriginalName = $file->getFilename();
}
$newFilename = Urlizer::urlize(pathinfo($getOriginalName, PATHINFO_FILENAME));
$this->filesystem->write(
self::ARTICLE_IMAGES.'/'.$newFilename,
file_get_contents($file->getPathname())
);
return $newFilename;
}
public function getPublicPath(string $path): string {
return '/uploads/'. $path;
}
}
`
I'm enjoying so much this course!
I appreciate your help !
Hey Raed!
Glad you're enjoying the course! But I'm sorry you're having trouble at this spot! Let's see if we can figure it out.
So, you said that you get this "File already exists at path" when you load the fixtures? Hmm, I think that's coming from Flysystem. And the error says "article_image/lightspeed" is the filename... which doesn't contain a file extension of any unique part of the URL. I would look at UploadHelper - it should (at this point in the tutorial) add a random string to the filename so that it's always unique. You can see us doing this on this code block: https://symfonycasts.com/screencast/symfony-uploads/fixtures-uploading#codeblock-c317fd7c09'
Let me know what you find out! I think there is something wrong with the $newFilename
part of the code.
Cheers!
Hey weaverryan,
Thank you so much for your help!
I don't how i missed out that uniqid part :-) Its working now after adding it, Thanks again !
Hey great tuts! btw could you pls tell how to get the url of the uploaded file on local/s3 storage?
Hi Tanvir mahmud R.!
Thanks :). Getting the URL to the uploaded file can be tricky - but we talk about it in various parts of this tutorial. Try checking out these:
* https://symfonycasts.com/sc...
* https://symfonycasts.com/sc...
* https://symfonycasts.com/sc...
* https://symfonycasts.com/sc...
Let me know if those help!
Cheers!
// 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
"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
"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.17.6
"symfony/form": "^4.0", // v4.2.3
"symfony/framework-bundle": "^4.0", // v4.2.3
"symfony/orm-pack": "^1.0", // v1.0.6
"symfony/security-bundle": "^4.0", // v4.2.3
"symfony/serializer-pack": "^1.0", // v1.0.2
"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/profiler-pack": "^1.0", // v1.0.4
"symfony/var-dumper": "^3.3|^4.0" // v4.2.3
}
}
Hey thank you fro the tuto, is it possible to change the upload destination directory from `public/uploads/` to somehting else !