开发者问题收集

Symfony2:使用 VichUploaderBundle 手动上传文件

2015-08-04
7673

如何在没有表单的情况下使用 VichUploaderBundle 上传文件?

我的某个目录中有文件(例如 web/media/tmp/temp_file.jpg

如果我尝试此操作:

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

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

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

我收到此错误:

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

我需要从远程 URL 上传文件。因此,我将文件上传到 tmp 目录(使用 curl),然后我继续尝试将其注入 VichUploadBundle(如上所示)。

3个回答

接受的答案不再正确?。根据 使用文档 您确实可以手动上传 File ,而无需使用 Symfony 的表单组件:

/**
 * 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');
    }
}
TMichel
2015-11-29

您应该使用 Symfony\Component\HttpFoundation\File\UploadedFile 而不是 File :

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

VichUploaderBundle 将处理此对象。

Dmitry
2016-08-12

要结合答案并解决 VichUploader 和 inject_on_load: truepostLoad 事件期间更改 updatedAt 的问题。

问题解释

由于 VichUploader 映射属性不受 Doctrine ORM 监控,您需要确保 ORM 管理的至少一个属性已更改以触发 VichUploader 用于管理文件引用的 prePersistpreUpdate 事件。
这通常是通过在文件设置器中使用 updatedAt DATETIME 列来完成的,但可以是任何由 ORM 管理的属性。

postLoad 事件设置器 File 实例问题

验证 UploadedFile 的实例是否提供给映射的属性设置器方法,将确保 updatedAt 表单提交期间或手动提供时更改,而不是在 postLoad 事件期间更改 - 其中 VichUpload 向相同的映射 setter 方法提供 File 的实例。否则,当 Doctrine 加载并管理 Image 对象的实例时,如果您调用 $em::flush() ,则会导致视图中的 updatedAt 值发生更改,并可能更改数据库值。

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');
        }
    }

}


然后,您可以通过实例化 UploadedFile 对象并将 error 参数设置为 null 并将 test 参数设置为 true 来手动定义要在实体中使用的文件,这将禁用验证 UPLOAD_ERR_OKis_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+

在 Symfony 4.1 及更高版本中,文件大小参数已被弃用。

use Symfony\Component\HttpFoundation\File\UploadedFile;

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

现在,您可以使用手动上传的文件成功保留和/或刷新您的实体。

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

$em->persist($image);
$em->flush();
Will B.
2020-10-21