Symfony2: manual file upload with VichUploaderBundle
Asked Answered
F

4

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).

Fredra answered 4/8, 2015 at 14:36 Comment(0)
P
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.

Pokpoke answered 5/8, 2015 at 11:47 Comment(1)
Oh, I was afraid of this. But it doesn't matter, I will figure out something else. Thank you very much!Fredra
G
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');
    }
}
Gallstone answered 29/11, 2015 at 12:43 Comment(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_destinationPrimate
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). ThxIre
S
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.

Ss answered 12/8, 2016 at 13:40 Comment(2)
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).Timothytimour
This should be the accepted answer, but as @Timothytimour 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.Baumbaugh
A
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();
Annulment answered 21/10, 2020 at 19:18 Comment(0)
P
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.

Pokpoke answered 5/8, 2015 at 11:47 Comment(1)
Oh, I was afraid of this. But it doesn't matter, I will figure out something else. Thank you very much!Fredra

© 2022 - 2024 — McMap. All rights reserved.