Skip to content

Inconsistent behavior of FileUpload in Form #27524

Closed
@Aerendir

Description

@Aerendir

To upload a file, the documentation shows this code:

class ProductController extends Controller
{
    /**
     * @Route("/product/new", name="app_product_new")
     */
    public function new(Request $request)
    {
        ...
        if ($form->isSubmitted() && $form->isValid()) {
            // $file stores the uploaded PDF file
            /** @var Symfony\Component\HttpFoundation\File\UploadedFile $file */
            $file = $product->getBrochure();

            $fileName = $this->generateUniqueFileName().'.'.$file->guessExtension();

            // moves the file to the directory where brochures are stored
            $file->move(
                $this->getParameter('brochures_directory'),
                $fileName
            );

            ...
        }

        ...
    }
}

But this code has an inconsistent behaviour.

In fact, if someone tries to upload a file that exceeds the limit for the file size set in the php.ini, an error is thrown:

FileNotFoundException: The file "" does not exist

This error is thrown by $file->guessExtension().

Nor the Form nor any other parts of the code, intercept the fact that the file is too big until we try to move it.

And so, in the end, we came up with an error that has nothing to do with the real problem (the file is too big).

This forced me to debug for about 40 minutes with a lot of try-and-fail until I understood the problem was a too big file.

And I was lucky: I'm refactoring some old code that use UploadedFile::getClientOriginalExtension(), so I knew I had to try with such method.

The problem, anyway, is that the Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser uses the content of the file to guess the extension, but if the file is too big such content is not available.

So there is a sort of circular reference:

  1. The file is too big and so is not available to PHP;
  2. To move the file, we need the guessed extension, but this is not guessable as the file doesn't exist: we get a really generic FileException "The file "" does not exist";
  3. The only way to get the real IniSizeFileException is calling UploadedFile::move()
  4. But we cannot call UploadedFile::move() (and get the real exception) because we first get the generic error caused by the guessing of the exception.

Finally, I don't know if this is a documentation issue or is a tricky bug of the framework: I'm starting writing here to get some feedback and to make you aware of the problem.

POSSIBLE SOLUTIONS

  1. Suggest using the unsafe UploadedFile::getClientOriginalExtension()
  2. Find a way to intercept earlier the error (maybe moving the file to the sys_get_temp_dir() dir) when calling UploadedFile::guessExtension() (maybe only if the "file too big" exception is thrown during guessing)

MORE PROBLEMS NOT SHOWN IN THE DOCS

Apart from what I said until now, there is another problem in the docs:

How to handle a "file too big" exception?

As the Form component cannot intercept this error (because a call to move_uploaded_file is required to get the exception thrown), once it happen, how do we show it to the end user?

The documentation should show a way to manually set the error in the form field, so on successive load of the page it is shown near the upload field.

Cross posted in symfony/symfony-docs#9885

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions