【问题标题】:Symfony2 file upload step by stepSymfony2文件上传一步一步
【发布时间】:2013-07-30 20:26:47
【问题描述】:

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

别担心,我已经检查过the documentation。真的很好,但是我的问题在任何教程中都没有解释。

我正在寻找有关如何使用 Symfony2 上传文件的指导,但包含每个人都需要的所有东西(例如扩展限制、基于 id 和内容重命名文件、将路径存储在 db 中等。 ..)

我找到了很好的教程,尝试将它们混合但没有成功。每次出现不同的问题:每次提交表单时都会重新上传文件(即使文件字段为空),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 实现文件上传表单和重命名功能的“经典”方式是什么?

【问题讨论】:

    标签: file symfony doctrine-orm upload formbuilder


    【解决方案1】:

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

    composer require vich/uploader-bundle
    

    Admission.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;
        }
    }
    

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

    【讨论】:

      【解决方案2】:

      我建议你使用vlabs media bundle

      【讨论】:

      • 完美运行 - 简单且有适当的文档。谢谢!
      • 我希望我能给这个不止一个赞成票。谢谢!
      【解决方案3】:

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

      请参阅official documentation 了解如何执行此操作。简单的文件上传有很好的工作示例。另请查看lifecycle callbacks 的教义文档。

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

      每个浏览器都有一些 HTML-Form 验证。有关input 元素中的HTML accept="" 属性,请参见this question。同样在 Symfony2 中,您可以使用此注解 specify the MIME-type 上传文件:

      /**
       * @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 之一。它应该是不言自明的。否则official docs 一如既往的帮助。

          /**
           * 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;
      }
      

      这是您的文件实际移动到正确目录的重要部分。请注意,我使用了一些其他方法。大家可以通过official docs获取。

      接下来你需要一个表格。表单类本身非常简单。只要确保你像这样设置默认的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。你可以找到它here。但是由于您说的是文件上传,所以我改用了File Constraint
      • 正如我在本文开头提到的,有许多 Bundle 可以为您处理所有这些事情。如果您想要轻松的生活,请查看它们。

      编辑:

      • DoctrineExtensionsBundle 更改为DoctrineBehaviours,因为旧版本的开发停止支持DoctrineBehaviours 捆绑包。

      【讨论】:

      • 太完美了!谢谢,我修改了你的代码,看来我终于明白它是如何工作的了。我第一次尝试只使用路径,但使用文件变量更好
      • 我被引导到这个问题的原因与 OP 类似。这个答案比官方文档更清楚!对你好,两年多之后,它仍然比文档更相关......我发现这样的事情是 Symfony 迄今为止唯一的问题。它很强大,但文档很差
      • 谢谢。由于 Symfony2(及其文档)是社区驱动的,如果您觉得某些内容解释得不够好,您可以轻松更新和改进它。
      • 有时 Symfony 官方文档有点麻烦。所以谢谢你的回答!这使得文件上传更容易理解
      • 在我看来 Symfony 文档不是很详细,因此他们可以为该服务提供咨询和收费。
      【解决方案4】:

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

      https://github.com/dustin10/VichUploaderBundle

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-10-04
        • 1970-01-01
        • 1970-01-01
        • 2016-01-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多