Skip to content

Cannot use #[MapUploadedFile] with optional file #58590

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

Open
jeremyhalin opened this issue Oct 17, 2024 · 11 comments
Open

Cannot use #[MapUploadedFile] with optional file #58590

jeremyhalin opened this issue Oct 17, 2024 · 11 comments

Comments

@jeremyhalin
Copy link

jeremyhalin commented Oct 17, 2024

Symfony version(s) affected

7.1.x

Description

I'm trying to use #[MapUploadedFile] for a POST route with multipart/form-data.
When I pass a file it's working as expected, but when I try to make it optional, the test fails because it receives an array while waiting for null | UploadedFile.

Is it expected? Maybe I'm just wrongly using the browser client to make a request without files?

How to reproduce

Controller

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapUploadedFile;
use Symfony\Component\Routing\Annotation\Route;

class UpdateController extends AbstractController
{
    #[Route('/', name: 'update', methods: ['POST'])]
    public function index(
        #[MapUploadedFile]
        ?UploadedFile $file,
    ): JsonResponse {
        dump($file);
        return $this->json('', Response::HTTP_OK);
    }
}

Test

<?php

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class UpdateControllerTest extends WebTestCase
{
    public function testPictureIsOptionnal(
    ): void {
        $client = static::createClient();
        $crawler = $client->request('POST', '/', [], [], ['CONTENT_TYPE' => 'multipart/form-data']);
        self::assertResponseIsSuccessful();
    }
}

Error

ErrorException: App\Controller\UpdateController::index(): Argument #1 ($uploadedPicture) must be of type ?Symfony\Component\HttpFoundation\File\UploadedFile, array given, called in /var/www/html/vendor/symfony/http-kernel/HttpKernel.php on line 183 in /var/www/html/src/Controller/UpdateController.php:1

Possible Solution

No response

Additional Context

No response

@steevanb
Copy link

steevanb commented Oct 17, 2024

@jeremyhalin can you dump $file when it's an array, to see what we have?

@jeremyhalin
Copy link
Author

@jeremyhalin can you dump $file when it's an array, to see what we have?

When I type mixed $picture, I have an empty array.

@jeremyhalin
Copy link
Author

jeremyhalin commented Oct 17, 2024

The more I test, the more I don't see how #[MapUploadedFile] can be used as optional.

@steevanb
Copy link

Can you create a simple repository on your github, with symfony installed and your use case?

It will be easier for us to test.

@jeremyhalin
Copy link
Author

The two code examples are all I have to share. If you want to test, you can install Symfony demo and just copy/paste my code.

@cizordj
Copy link

cizordj commented Oct 22, 2024

I'm having a similar problem.

RafinhaApi\Controller\FileController::uploadFile(): Argument #1 ($picture) must be of type Symfony\Component\HttpFoundation\File\UploadedFile, array given

This is the code:

public function uploadFile(
    #[MapUploadedFile(
        [
            new Assert\File(mimeTypes: ['image/png', 'image/jpeg', 'image/webp', 'image/jpg']),
            new Assert\Image(maxWidth: 3840, maxHeight: 2160),
        ],
        'picture',
    )] UploadedFile $picture // <- $picture is an empty array
): JsonResponse {
    return $this->json([]);
}

This is my request:

curl --request PUT \
  --url http://localhost:8000/api/file/ \
  --header 'Content-Type: multipart/form-data' \
  --header 'User-Agent: insomnia/10.0.0' \
  --form picture=@/run/user/1000/doc/f67e3a20/3868846673715350409_n.jpg

To reproduce this issue you can go to my repository
https://codeberg.org/cizordj/rafinha-api

and run the application:

cd public
symfony local:server:start --no-tls

@jeremyhalin
Copy link
Author

Hello @cizordj

It seems you are having the problem even if you pass the file, which is different from my case when I try to call the route without passing a file with an optional UploadedFile. In my case, passing a file works.

@jeremyhalin
Copy link
Author

Seems related to #54871

@mariusjp
Copy link

mariusjp commented Dec 12, 2024

This part of the RequestPayloadValueResolver (line 235)

private function mapUploadedFile(Request $request, ArgumentMetadata $argument, MapUploadedFile $attribute): UploadedFile|array|null
{
    return $request->files->get($attribute->name ?? $argument->getName(), []);
}

assumes that files are present, even though the ArgumentMetaData is right there to check if we have set the parameter as null.

Not sure though if this kind of logic should be implemented here.

On the other hand, the empty array that is given as parameter to $request->files->get() is a default return...

@florian2peaches
Copy link

Is MapUploadedFile meant to be used for multiple file-upload? then an empty array is slightly sensible as a default return.
Even better would be adding MapUploadedFiles or sth

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

No branches or pull requests

7 participants