Symfony2: manual file upload with VichUploaderBundle

Accepted answer is not correct (anymore?). According with the usage documentation you can indeed manually upload a File without using Symfony's Form Component:

/**
 * If manually uploading a file (i.e. not using Symfony Form) ensure an instance
 * of 'UploadedFile' is injected into this setter to trigger the  update. If this
 * bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
 * must be able to accept an instance of 'File' as the bundle will inject one here
 * during Doctrine hydration.
 *
 * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
 */
public function setImageFile(File $image = null)
{
    $this->imageFile = $image;

    if ($image) {
        // It is required that at least one field changes if you are using doctrine
        // otherwise the event listeners won't be called and the file is lost
        $this->updatedAt = new \DateTime('now');
    }
}

You should use Symfony\Component\HttpFoundation\File\UploadedFile instead of File:

$file = new UploadedFile($filename, $filename, null, filesize($filename), false, true);

VichUploaderBundle will handle this object.


To combine the answers and address an issue with VichUploader and inject_on_load: true changing the updatedAt during the postLoad event.

Explanation of the issue

Since the VichUploader mapped property is not monitored by Doctrine ORM, you will need to ensure at least one of the properties that the ORM manages is changed to trigger the prePersist or preUpdate events that VichUploader uses to manage the file reference.
This is generally done by using an updatedAt DATETIME column in your file setter but can be any property that is managed by the ORM.

postLoad event setter File instance issue

Verifying an instance of UploadedFile is supplied to the mapped property setter method, will ensure that updatedAt is changed only during Form submissions or when manually supplied, instead of during the postLoad event - where VichUpload supplies an instance of File to the same mapped setter method. Which would otherwise cause the updatedAt value in your views to change and potentially change the database value if you call $em::flush() when instances of the Image object are loaded by Doctrine and therefor managed.

App\Entity\Image

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;

/**
 * @ORM\Entity
 * @Vich\Uploadable
 */
class Image
{

    /**
     * @var \DateTime
     * @ORM\Column(name="updated_at", type="datetime")
     */
    private $updatedAt;

    /**
     * @var \Symfony\Component\HttpFoundation\File\File
     * @Vich\UploadableField(mapping="your_mapping", fileNameProperty="image")
     */
    private $imageFile;

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

    public function __construct()
    {
        $this->updatedAt = new \DateTime('now');
    }

    //...

    public function setImageFile(File $image = null)
    {
        $this->imageFile = $image;
        if ($image instanceof UploadedFile) {
            $this->updatedAt = new \DateTime('now');
        }
    }

}


Then you can manually define the file to use in your entity by instantiating an UploadedFile object and setting the error argument as null and the test argument to true, which disables validating UPLOAD_ERR_OK and is_uploaded_file() [sic].

Symfony <= 4.0

use Symfony\Component\HttpFoundation\File\UploadedFile;

$file = new UploadedFile($path, $filename, null, filesize($path), null, true);

Symfony 4.1+

In Symfony 4.1 and later, the file size argument was deprecated.

use Symfony\Component\HttpFoundation\File\UploadedFile;

$file = new UploadedFile($path, $filename, null, null, true);

Now you can successfully persist and/or flush your entity with the manually uploaded file.

$image = new Image();
$image->setImageFile($file);

$em->persist($image);
$em->flush();