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.

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

  • 2020-06-24 Zool

    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 !

  • 2020-06-24 weaverryan

    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!

  • 2020-06-23 Zool

    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 !

  • 2020-03-15 weaverryan

    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!

  • 2020-03-11 Tanvir Mahmud Rabbi

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

  • 2019-09-16 Vladimir Sadicov

    Hey Ahmedbhs

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

    Cheers!

  • 2019-09-14 Ahmedbhs

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