55

我还在学习 Symfony2 并且不明白如何上传文件。

别担心,我已经检查了文档。这真的很好,但我的问题没有在任何教程中解释。

我正在寻找有关如何使用 Symfony2 上传文件的指导,但具有每个人都需要的所有内容(例如扩展名的约束、基于 id 和内容的文件重命名、将路径存储在数据库中等...)

我找到了很好的教程,尝试将它们混合但没有成功。每次出现不同的问题:每次提交表单时都会重新上传文件(即使文件字段为空),guessExtension 无法使用,tmp 路径存储在数据库中而不是正确的路径,文件没有移动,不可能在重命名中使用了 id,因为 id 是自动递增的,因此尚未生成)。

所以,我会放一个“标准”实体,比如说:Photo.php

/**
 * Photo
 *
 * @ORM\Table(name="photo")
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks
 */
class Photo
{
    // Annotation for the id and auto increment etc
    private $id;

    /**
     * @var string
     * @Assert\File( maxSize = "3072k", mimeTypesMessage = "Please upload a valid Image")
     * @ORM\Column(name="image", type="string", length=245, nullable=false)
     */
    private $image

    private $title

    private $description

    // all the function get, set for the 4 previous variables
}

和控制器:

public function addPhotoAction()
{
    $add_photo = new Photo;
    $formBuilderPhoto = $this->createFormBuilder($add_photo);
    $formBuilderPhoto
        ->add('title','text',array('label'  => 'Title of the photo', 'required' => true))
        ->add('image','file', array('required' => true, 'data_class' => null))
        ->add('description','textarea',array('label' => 'Description of your photo', 'required' => false))
    ;

    $form_photo = $formBuilderPhoto->getForm();

    if ($request->getMethod() == 'POST') {
        $form_photo->bind($request);
        if ($form_photo->isValid()) {
            // ...
        }
    }
    return $this->render('MyBundle:frontend:photo.html.twig',
        array('form_photo' => $form_photo->createView())
    );
}

您现在知道要添加哪些“重要”功能才能上传照片并重命名吗?

您如何检查扩展程序以查看是否可以上传?

你用 Symfony2 做这样的事情的实际方法是什么?我知道有很多 Bundle 可以为你做所有这些事情,但我想学习如何去做并理解这个过程。

用 Symfony2 实现文件上传表单和重命名功能的“经典”方法是什么?

4

4 回答 4

108

您现在知道要添加哪些“重要”功能才能上传照片并重命名吗?

有关如何执行此操作,请参阅官方文档。简单的文件上传有很好的工作示例。还要检查关于生命周期回调的原则文档。

您如何检查扩展程序以查看是否可以上传?

每个浏览器都有一些 HTML-Form 验证。有关元素中的 HTML属性,请参阅此问题。同样在 Symfony2 中,您可以使用此注解指定上传文件的 MIME 类型:accept=""input

/**
 * @Assert\File(
 *     maxSize = "1024k",
 *     mimeTypes = {"application/pdf", "application/x-pdf"},
 *     mimeTypesMessage = "Please upload a valid PDF"
 * )
 */

即使您不想使用任何捆绑软件,我也必须向您推荐KnpDoctrineBehavioursBundle,它使文件上传更容易。


一步步:

因为您已经阅读了文档,所以我将逐步为您提供代码示例。

首先你需要一个实体。让我们称之为Image

/**
 * Class Image
 *
 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks
 */
class Image extends BaseEntity
{

注意@ORM\HasLifecycleCallbacks注释。这非常重要,您以后需要它。我们创建所有基本字段,例如ID和不。我们还需要一个字段来存储文件路径:

    /**
     * Image path
     *
     * @var string
     *
     * @ORM\Column(type="text", length=255, nullable=false)
     */
    protected $path;
    

一个用于图像本身。在这里,我们还定义了图像的验证。在我的示例中,它必须5M很大并且是定义的mimeTypes. 它应该是不言自明的。否则官方文档会一如既往地提供帮助。

    /**
     * Image file
     *
     * @var File
     *
     * @Assert\File(
     *     maxSize = "5M",
     *     mimeTypes = {"image/jpeg", "image/gif", "image/png", "image/tiff"},
     *     maxSizeMessage = "The maxmimum allowed file size is 5MB.",
     *     mimeTypesMessage = "Only the filetypes image are allowed."
     * )
     */
    protected $file;

使用以下命令添加所有Getters & Setters并更新您的数据库架构:

php app/console doctrine:schema:update --force

接下来我们需要生命周期。它们是Entity在某些事件上调用的方法。例如,@ORM\PreUpdate()方法之前的注释表示该方法在实体更新之前被调用。

/**
 * Called before saving the entity
 * 
 * @ORM\PrePersist()
 * @ORM\PreUpdate()
 */
public function preUpload()
{   
    if (null !== $this->file) {
        // do whatever you want to generate a unique name
        $filename = sha1(uniqid(mt_rand(), true));
        $this->path = $filename.'.'.$this->file->guessExtension();
    }
}

在实体被存储或更新之前,调用此方法。例如,您可以使用它来生成唯一的文件名。

/**
 * Called before entity removal
 *
 * @ORM\PreRemove()
 */
public function removeUpload()
{
    if ($file = $this->getAbsolutePath()) {
        unlink($file); 
    }
}

在实体被删除之前调用。这使您有时间从文件夹中删除图像或根据需要记录消息。

/**
 * Called after entity persistence
 *
 * @ORM\PostPersist()
 * @ORM\PostUpdate()
 */
public function upload()
{
    // The file property can be empty if the field is not required
    if (null === $this->file) {
        return;
    }

    // Use the original file name here but you should
    // sanitize it at least to avoid any security issues

    // move takes the target directory and then the
    // target filename to move to
    $this->file->move(
        $this->getUploadRootDir(),
        $this->path
    );

    // Set the path property to the filename where you've saved the file
    //$this->path = $this->file->getClientOriginalName();

    // Clean up the file property as you won't need it anymore
    $this->file = null;
}

这是您的文件实际移动到正确目录的重要部分。请注意,我使用了一些其他方法。您都可以从官方文档中获得它们。

接下来你需要的是一个表格。表单类本身非常简单。只要确保你data_class像这样设置默认值:

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(
        array(
            'data_class' => 'FSchubert\SiyabongaBundle\Entity\Image',
       )
    );
}

在方法中可以很容易地创建文件上传字段buildForm()

$builder->add('file', 'file');

你的方法Controller有点长,只是在这里粘贴它们,恕我直言,这不是回答你问题的一部分。有无数的例子可以Controller Action为你的目的写一个合适的。


您必须记住的更多事项:

  • 您需要向app上传文件的文件夹授予写入权限。虽然很明显,如果您有多个运行应用程序的服务器,这可能会很烦人。
  • 您的实体也有一个Image Constraint。你可以在这里找到它。但是由于您在谈论文件上传,所以我改用了File Constraint
  • 正如我在这篇文章的顶部提到的,有许多 Bundles 可以为您处理所有这些事情。如果您想要轻松的生活,请查看它们。

编辑:

  • 从 更改为DoctrineExtensionsBundleDoctrineBehaviours因为旧版本的开发停止支持DoctrineBehaviours捆绑包。
于 2013-07-30T19:41:11.120 回答
12

我建议您使用vlabs 媒体包

于 2013-07-31T02:43:35.113 回答
6

VichUploaderBundle 也很容易用于上传文件:

https://github.com/dustin10/VichUploaderBundle

于 2015-02-05T10:25:01.907 回答
0

我推荐 VichUploader 捆绑包和此代码,并将捆绑包植入实体和 FormType。

composer require vich/uploader-bundle

录取.php

/**
 * @ORM\Entity(repositoryClass=AdmissionRepository::class)
 * @Vich\Uploadable
 */
class Admission
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $cin;

    /**
     * @Vich\UploadableField(mapping="product_image", fileNameProperty="cin")
     * @var File
     */
    private $cinFile;


    public function getId(): ?int
    {
        return $this->id;
    }

    public function getCin(): ?string
    {
        return $this->cin;
    }

    public function setCin(string $cin): self
    {
        $this->cin = $cin;

        return $this;
    }
}

准入类型.php

   class AdmissionType extends AbstractType
      {
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('cinFile', VichFileType::class);

    }

vich_uploader.yaml

vich_uploader:
    db_driver: orm

    mappings:
        product_image:
            uri_prefix: /uploads
            upload_destination: '%kernel.project_dir%/public/uploads'
            inject_on_load: false
            delete_on_update: true
            delete_on_remove: true
            namer: vich_uploader.namer_origname
于 2021-03-29T02:32:07.617 回答