Upload file with VichUploader and ajax/axios

1.1k Views Asked by At

I am trying to set up a form to upload a file with VichUploader & axios/ajax.

The form I am sending looks like this on the console: the form on chrome debugger

I have set up vich uploader and GNP properly as the form works in non-ajax forms.

I have a simple file to upload with an Entity set like the following:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Vich\UploaderBundle\Mapping\Annotation as Vich;

/**
 * Class Logo
 *
 * @ORM\Table(name="logos")
 * @ORM\Entity()
 * @Vich\Uploadable()
 */
class Logo
{
    /**
     * @var int
     *
     * @ORM\Column(type="integer", name="id")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $logoName;

    /**
     * NOTE: This is not a mapped field of entity metadata, just a simple property.
     *
     * @var File
     *
     * @Vich\UploadableField(mapping="logo", fileNameProperty="logoName", size="logoSize")
     */
    private $logoFile;

    /**
     * @ORM\Column(type="integer", nullable=true)
     *
     * @var integer
     */
    private $logoSize;

    /**
     * @ORM\Column(type="datetime", nullable=true)
     */
    private $updatedAt;

The getters and setters are exactly like on the documentation

My FormType is also very basic:

<?php

// src/Form/UserType.php
namespace App\Form;

use App\Entity\Logo;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;

use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichFileType;

class LogoType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // Remember username is email
        $builder
            ->add('logoFile', VichFileType::class, [
                'label'         => false,
                'required'      => false,
                'allow_delete'  => false,
                'download_uri'  => true,
                'download_label' => true,
                'download_link' => true,
            ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => Logo::class,
            'csrf_protection' => false,
        ));
    }
}

And so is my controller:

    /**
     * Update a Logo for the Startup
     *
     * @Rest\Post("/logo")
     *
     * @param Request $request
     * @param UserRepository $userRepository
     *
     * @return Response
     */
    public function updateLogo(Request $request, UserRepository $userRepository): Response
    {
        $success = false;

        // Retrieve the user
        $user = $this->getUser();

//        var_dump($image);
        $startup = $user->getStartup();


        $logo = new Logo();
        $form = $this->createForm(LogoType::class, $logo);

        // Handle Submit (will only happen on POST)
        $form->submit($request->request->get('upload'));
        if ($form->isSubmitted() && $form->isValid()) {

            //Initialize EntityManager to update the user
            $entityManager = $this->getDoctrine()->getManager();

            $logo = $form->getData();
            $startup->setLogo($logo);

            try {
                $entityManager->persist($startup);
                $entityManager->flush();
            } catch (ORMException $e) {
                throw new HttpException(500, "Couldn't update your profile. Try again.");
            }

            $success = true;
        }

        $view = $this->view($success, 200);

        return $this->handleView($view);
    }

When doing this, it does create a new object Logo but all the fields are NULL and no file is uploaded to AWS S3. Note that if I pass logoFile instead of upload[logoFile] the form doesn't validate. I guess passing upload[logoFile] is the right thing to do.

Not sure what is happening under the hood. What Am I missing?

Thank you very much.

EDIT: Here is my ajax submission code - Handled with Axios on React:

const formData = new FormData();
    formData.append('upload[logoFile]', e.files[0]);

    axios.post('/startups/logo', formData, {
      headers: {
        'Content-Type': 'multipart/form-data',
      },
    })

EDIT2: Doing a dd($request->files) gives me the following:

StartupsController.php on line 152:
FileBag^ {#73
  #parameters: array:1 [
    "upload" => array:1 [
      "logoFile" => UploadedFile^ {#63
        -test: false
        -originalName: "49138054_2423211744374313_3487352288589119488_n.jpg"
        -mimeType: "image/jpeg"
        -error: 0
        path: "/private/var/folders/wl/n382y8xs021dnbz3jhbklsnm0000gn/T"
        filename: "phpWomX0H"
        basename: "phpWomX0H"
        pathname: "/private/var/folders/wl/n382y8xs021dnbz3jhbklsnm0000gn/T/phpWomX0H"
        extension: ""
        realPath: "/private/var/folders/wl/n382y8xs021dnbz3jhbklsnm0000gn/T/phpWomX0H"
        aTime: 2019-12-11 22:02:51
        mTime: 2019-12-11 22:02:51
        cTime: 2019-12-11 22:02:51
        inode: 8664431786
        size: 106292
        perms: 0100600
        owner: 501
        group: 20
        type: "file"
        writable: true
        readable: true
        executable: false
        file: true
        dir: false
        link: false
      }
    ]
  ]
}
0

There are 0 best solutions below