Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Symfony FileValidator constraint is not working with flysystem v2 #1217

Open
VincentLanglet opened this issue Jul 20, 2021 · 20 comments
Open
Labels

Comments

@VincentLanglet
Copy link
Contributor

Bug Report

Q A
BC Break no
Bundle version 1.18.0
Symfony version 4.4.26
PHP version 7.4

Summary

Hi @garak, I started to use this bundle with flysystem v2. It works well for uploading and downloading files.
But it seems like none of my entity validation are passing now.

The FileValidator of Symfony is checking for is_file($path) and the file is never found.
With gaufrette, the file was
image

The path is coming from https://github.com/dustin10/VichUploaderBundle/blob/master/src/Storage/GaufretteStorage.php#L77, and in the FileValidator is_path were returning true for this value.

With the flysystem, the file is now
image
And now the check done by the FileValidator with is_file is returning false. It seems normal because no prefix is added to say to use flysystem and to precise the upload destination.

It doesn't seems easy to fix this, because the resolvePath is used in multiple places, and I assume for instance that the resolveStream method is working well since I can download files.

public function resolveStream($obj, string $fieldName, ?string $className = null)

So it would mean that sometimes, we need the "naked" path and sometimes we would need something more elaborate in order to have is_file returning true for the path. Maybe @frankdejonge could help us on the question "Which path should be use with flysystem in order to have is_file($path) returning true".

Maybe I'm taking this problem the wrong way, and we should focus about the place VichUploaderBundle is generating the file (which symfony is validating). If I understand correctly, it's done here:

public function injectFile($obj, PropertyMapping $mapping): void
{
$path = $this->storage->resolvePath($obj, $mapping->getFilePropertyName());
if (null !== $path) {
$mapping->setFile($obj, new File($path, false));
}
}

I would say, the file should be generated differently for Flysystem. But if we want to avoid downloading the file everytime we're loading the entity, just in order to have a correct path, we end with the same question, "Which path should be use with flysystem in order to have is_file($path) returning true". Don't know if a new method should be added to the flysystem API or flysystem-bundle API (cc @tgalopin)

@fullbl, you added the support for flysystem 2 in #1179, don't you have the same issue ? Any idea how to fix it ?

@garak garak added the Bug label Jul 27, 2021
@garak
Copy link
Collaborator

garak commented Jul 27, 2021

Unfortunately, my knowledge of Flysystem is not sufficient to give an opinion here.
I hope that some of the mentioned people can chime in and help.

@Padam87
Copy link

Padam87 commented Aug 21, 2021

This was broken in #1179.

https://github.com/dustin10/VichUploaderBundle/pull/1179/files#diff-2b8001f2c9bd1b9232145a0f4f8f046185f9449271c43eda05b15b03d369fb12L70

The absolute part has been removed.

This method is used by the FileInjector: https://github.com/dustin10/VichUploaderBundle/blob/master/src/Injector/FileInjector.php#L29

Unless I'm missing something this would be a hard fix, since flysystem doesn't provide a stream wrapper, unlike gaufrette.
Until this is fixed, I would consider the flysystem storage as unsafe to use, since workarounds are needed to adapt to the situation.

You cannot rely on the injected files at all.

@frankdejonge
Copy link

frankdejonge commented Aug 22, 2021

Just for the record, Flysystem has never supplied a stream wrapper. There is a fileExists method on Flysystem's Filesystem object which tells you if a file exists or not. Also, the absence of a stream wrapper does not make Flysystem unsafe for use. Seems like a bit if a dramatic thing to say.

@VincentLanglet
Copy link
Contributor Author

Just for the record, Flysystem has never supplied a stream wrapper.

I never try with the v1, but seems like VichUploader had a way to do as-if.

There is a fileExists method on Flysystem's Filesystem object which tells you if a file exists or not.

Sure, there are method for this, which are useful in a personal and specific usage.

The issue was to be compatible with the generic check is_file done by the FileValidator of Symfony.
https://github.com/symfony/symfony/blob/5.4/src/Symfony/Component/Validator/Constraints/FileValidator.php#L125

Also, the absence of a stream wrapper does not make Flysystem unsafe for use. Seems like a bit if a dramatic thing to say.

I think what he wanted to say is that it's unsafe to use with VichUploaderBundle, since they are currently not compatible.
In my case, I soon as I started to move from gaufrette to flysystem, all my files were reported as not found by validation constraints.

@fullbl
Copy link
Contributor

fullbl commented Aug 22, 2021 via email

@VincentLanglet
Copy link
Contributor Author

In the V1 of Flysystem,
Calling FilesystemAdapter::applyPathPrefix method was kinda a hack since it wasn't in the interface.

if (\is_callable([$adapter, 'applyPathPrefix'])) {
    return (string) $adapter->applyPathPrefix($path);
}

But it could still be used with V2.

The main issue with V2 is that the method Filesystem::getAdapter was removed.

Sorry for the delay. Seems to me that an alternative could be to provide a custom validator (or improve Symfony's), I don't see how is_file could work with a file that is not present on the server's filesystem!

Indeed

@Padam87
Copy link

Padam87 commented Aug 22, 2021

The best solution would be for flysystem to provide a stream wrapper, like gaufrette.

@Padam87
Copy link

Padam87 commented Aug 22, 2021

Just for the record, Flysystem has never supplied a stream wrapper.

@frankdejonge I've seen in the flysystem issues it has been brought up a few times.

I think you should consider it. It would make working with flysystem much easier for symfony devs, as we could use symfony's File object.

I've put one together quickly, https://gist.github.com/Padam87/3825951526bd10a735dedfa26a3bf4ed

(It has issues, like the times etc - requires work)

But Symfony's file is now usable.

        dump(new File('flysystem://contract_document/'.$contractDocument->getFileName())); die();

Screenshot 2021-08-22 at 22-50-44 Screenshot

After this is done, Vich Uploader could simply use the same logic as the gaufrette storage. https://github.com/dustin10/VichUploaderBundle/blob/master/src/Storage/GaufretteStorage.php#L78

BTW, this would allow us to drop gaufrette, as that projects seems to be EOL (or at least taking a break)

@frankdejonge
Copy link

There are community provided stream-wrappers available. I'm not interested in maintaining one. Stream wrappers have to make trade-offs, more severe ones than the normal filesystem trade-offs need. Maintaining one will be a constant battle between opinions while I personally think they should not be used and I'm not looking to get that burden on my shoulders. I personally think it's a design flaw that a local filesystem needs to be faked, often with severe performance ramifications, to be able to check if something is a file. The tight coupling with symfony's internals causes this, so I think a better way to deal with it is to address the root cause, which is IMO not the absence of a stream wrapper. Maintaining Flysystem is mostly a one-man thin. I'm grateful that I'm getting more help now with a couple of adapters, but I can only spend my time once, and I don't feel much for spending it on this.

@Padam87
Copy link

Padam87 commented Aug 22, 2021

@frankdejonge Time is always limited on OS projects, and I fully understand why you wouldn't want to maintain a wrapper.

Unfortunately community packages are outdated, and I would imagine @garak would be against relying on one of those in the first place.

Welp, I don't really see any other way to do this without a stream wrapper.

@tgalopin
Copy link
Contributor

Perhaps an intermediate solution could be to provide the stream wrapper in flysystem-bundle? I guess it would make sense as it'll mostly used by Symfony devs and I'd be fine maintaining it.

@VincentLanglet
Copy link
Contributor Author

Perhaps an intermediate solution could be to provide the stream wrapper in flysystem-bundle? I guess it would make sense as it'll mostly used by Symfony devs and I'd be fine maintaining it.

This would be awesome.

If I understand correctly, you know how to do it @Padam87 ? Do you mind doing a PR on the flysystem bundle ?

@frankdejonge
Copy link

@tgalopin if you want to maintain it I can create a separate repository for this so people can depend on the stream wrapper directly. Let's touch base on the slack 👍

@tgalopin
Copy link
Contributor

As discussed with Frank, I'm working on a small lib implementing a stream wrapper for Flysystem. I hope to release a first version soon.

@VincentLanglet
Copy link
Contributor Author

As discussed with Frank, I'm working on a small lib implementing a stream wrapper for Flysystem. I hope to release a first version soon.

Hi @tgalopin ; did you have time to implement the stream wrapper ? :)

@tgalopin
Copy link
Contributor

Hello Vincent,

I started to do so but didn't manage to finish yet. I'll push initial code soon so that you can contribute if you wish.

@kor3k
Copy link

kor3k commented Nov 23, 2021

@VincentLanglet
Copy link
Contributor Author

As discussed with Frank, I'm working on a small lib implementing a stream wrapper for Flysystem. I hope to release a first version soon.

With gaufrette being deprecated, having an "official" stream-wrapper for flysystem seems even more useful now.

I started to do so but didn't manage to finish yet. I'll push initial code soon so that you can contribute if you wish.

What is the current state/link of the WIP library ?

@SebastienTainon
Copy link

This seems to be a blocking issue for anyone wanting to use VichUploaderBundle in conjunction with a Flysystem abstraction and Symfony Validator constraints like @Assert\Image(maxSize = "5M"), a situation I'm currently facing too...

@ostrolucky
Copy link

I think root issue might be somewhere else. Of course it would be good if you could still read file after bundle injects it with native file functions, but for most cases it's not necessary: VichUploaderBundle replaces file instance before entity load, or pre-persist, so after entity was already validated. However, in some cases Validation is afterwards triggered again. For example, this happens if you use API Platform with custom controller for file upload, but you didn't turn off validation for this endpoint. Solution for me was

new Post(
    uriTemplate: '/users/file_import',
    controller: UserFileImportCreateController::class,
+   validate: false,
),

This way second validation is not triggered and first validation passes, because at that point file is instance of UploadedFile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

9 participants