8

How can I upload file with VichUploaderBundle without form?

I have file in some directory (for example web/media/tmp/temp_file.jpg)

If I try this:

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

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

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

I've got this error:

The file "temp_file.jpg" was not uploaded due to an unknown error.

I need to upload file from remote url. So I upload file to tmp direcotry (with curl) and then I keep trying to inject it to VichUploadBundle (as you can see above).

Tomas S.
  • 329
  • 2
  • 9

4 Answers4

11

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');
    }
}
nck
  • 41
  • 3
  • 7
TMichel
  • 4,336
  • 9
  • 44
  • 67
  • 2
    `UploadedFile` object must have test flag set to true if you want to "upload" a local stored file, e.g.: migrating files from a local dir to VichUploader configured upload_destination – Manuel Jun 16 '19 at 23:47
  • how do you upload the file? When I do SetImageFile, the DB gets filed but the file never is uploader to my destination folder (on S3). Thx – Miles M. Dec 11 '19 at 21:59
4

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.

Dmitry
  • 7,457
  • 12
  • 57
  • 83
  • For completeness it would be better update your answer explaining the code and/or preferably with adding a link pointing to the relative documentation on github if it exist (or make a PR if not). – gp_sflover Aug 12 '16 at 14:01
  • 3
    This should be the accepted answer, but as @gp_sflover pointed out, it should be edited to further explain how it works: the last parameter (set mode test = true) allows using UploadedFile without actually uploading a file, since the file is already on the server. – Roubi Jan 14 '19 at 16:16
3

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();
Will B.
  • 17,883
  • 4
  • 67
  • 69
1

Short answer: VichUploaderBundle does not support uploading files without using Symfony Form component.

I need to upload file from remote url. So I upload file to tmp direcotry (with curl) and then I keep trying to inject it to VichUploadBundle (as you can see above).

In that case, you don't need to upload, you need to download. And that's not what VichUploaderBundle is meant to do.

K-Phoen
  • 1,260
  • 9
  • 18
  • Oh, I was afraid of this. But it doesn't matter, I will figure out something else. Thank you very much! – Tomas S. Aug 05 '15 at 16:22