Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Using the Filesystem

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.

Start your All-Access Pass
Buy just this tutorial for $10.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Config 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!

Binding the Filesystem for Autowiring

Unused binding $uploadsPath in service UniqueUserValidator.

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() references FilesystemInterface 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.

Leave a comment!

31
Login or Register to join the conversation

Hey thank you fro the tuto, is it possible to change the upload destination directory from `public/uploads/` to somehting else !

1 Reply

Hey Ahmedbhs

Of course it's possible =) Check out previous chapter it was configured there! https://symfonycasts.com/sc...

Cheers!

Reply
Petru L. Avatar
Petru L. Avatar Petru L. | posted 1 year ago

Hey weaverryan ,

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
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.

Reply

Hey Petru Lebada

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!

Reply
Petru L. Avatar

yeah... i don't know how i've missed that, i must have been tired, sorry and thank you

Reply

Hey =) no problem! Keep asking questions it helps to learn and develop better apps!

Reply

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.

Reply

Hey Stéphane

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!

Reply

Hey Diego Aguiar,

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.

Reply

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...

Reply

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/... ?

Reply

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!

Reply
Default user avatar
Default user avatar Diego Riccetto | posted 1 year ago

Hello team and thanks for the tutorial,

I'm stuck in this part, in the method

Reply
Default user avatar

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!

Reply
Default user avatar

I solved...

Was the oneup_flysystem.yaml, i wrote wrong the destination.

Sorry

1 Reply

I'm glad to hear that you could fix your problem. Keep it going :)

Reply
Abdul mannan Avatar
Abdul mannan Avatar Abdul mannan | posted 1 year ago

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.
Reply

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!

Reply
Benoît M. Avatar
Benoît M. Avatar Benoît M. | posted 1 year ago

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

Reply

Hey benit

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!

Reply
Benoît M. Avatar

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

Reply

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!

Reply
Benoît M. Avatar

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

Reply

Awesome! One more output please php bin/console debug:config oneup_flysystem

Reply
Benoît M. Avatar

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: { }

Reply

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

Reply
Raed Avatar

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,


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 !

Reply

Hey Zool!

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/sc...

Let me know what you find out! I think there is something wrong with the $newFilename part of the code.

Cheers!

Reply
Raed Avatar

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 !

Reply
Tanvir mahmud R. Avatar
Tanvir mahmud R. Avatar Tanvir mahmud R. | posted 2 years ago

Hey great tuts! btw could you pls tell how to get the url of the uploaded file on local/s3 storage?

Reply

Hi Tanvir Mahmud Rabbi!

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!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

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
        "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
    }
}