如何使用 symfony2“手动”处理文件上传?

Posted

技术标签:

【中文标题】如何使用 symfony2“手动”处理文件上传?【英文标题】:how to "manually" process file upload with symfony2? 【发布时间】:2016-02-07 14:47:04 【问题描述】:

因为我有一个自定义构建的 jQuery 插件来将文件上传传递到我的 symfony2 webapp,所以我正在寻找在控制器中处理此上传的方法。

我目前拥有的标准(非 ajax)文件上传(并且适用于同步调用)看起来像这样

控制器摘录

    ...

    $entity  = new Image();
    $request = $this->getRequest();
    $form    = $this->createForm(new ImageType($createAction), $entity);
    $form->bind($request); // <-- Find a way to make this connection manually?!

    //check that a file was chosen
    $fileExists = isset($entity->file);

    if ( ($form->isValid()) && ($fileExists) ) 

        $em = $this->getDoctrine()->getManager();

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

    

    ...

表单类型:表单只需要文件和名称:

class ImageType extends AbstractType


    ...

    public function buildForm(FormBuilderInterface $builder, array $options)
    

        $createAction = $this->createAction;

        if ($createAction)         
            $builder
                ->add('file')
            ;
        

        $builder
            ->add('name', 'text', array('label' => 'Namn'))
        ;
    

    ...


据我了解(或者换句话说,显然不了解)带有 symfony2 和原则的文件上传系统,在这个调用中隐藏着相当多的魔法

$form->bind($request);

例如,如果我跳过这个 bind() 而是尝试像这样手动创建 Image 实体...

    $request = $this->getRequest();
    $parent = $request->request->get('parent');
    $file = $request->request->get('file1');
    $name = $request->request->get('name');

    $entity->setName( $name );
    $entity->setFile( $file );
    $entity->setFolder( null );

...我发现它甚至没有 setFile() ,因此可以通过其他方式进行处理。这是图像实体

namespace BizTV\MediaManagementBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * BizTV\MediaManagementBundle\Entity\Image
 *
 * @ORM\Table(name="image")
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks
 */
class Image

    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string $name
     *
     * @ORM\Column(name="name", type="string", length=255)
     * @Assert\NotBlank(message = "Bilden måste ha ett namn")
     */
    private $name;

    /**
     * @var integer $width
     *
     * @ORM\Column(name="width", type="integer")
     */
    private $width;

    /**
     * @var integer $height
     *
     * @ORM\Column(name="height", type="integer")
     */
    private $height;

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


//The deleteRequested variable is to flag that an image has been deleted by user.
//Due to slideshow issues we can however not delete the image right away, we can't risk to remove it from the
//cache manifest before the slideshow round is up.

    /**
     * @var time $deleteRequested
     * 
     * @ORM\Column(name="delete_requested", type="datetime", nullable=true)
     */
    private $deleteRequested;


    /**
    * @var object BizTV\BackendBundle\Entity\company
    *  
    * @ORM\ManyToOne(targetEntity="BizTV\BackendBundle\Entity\company")
    * @ORM\JoinColumn(name="company", referencedColumnName="id", nullable=false)
    */
    protected $company;     

    /**
    * @var object BizTV\MediaManagementBundle\Entity\Folder
    *  
    * @ORM\ManyToOne(targetEntity="BizTV\MediaManagementBundle\Entity\Folder")
    * @ORM\JoinColumn(name="folder", referencedColumnName="id", nullable=true)
    */
    protected $folder;


    /**
     * @Assert\File(maxSize="12000000")
     */
     public $file;

    /**
     * @ORM\OneToOne(targetEntity="BizTV\MediaManagementBundle\Entity\QrImage", mappedBy="image")
     */
    protected $qr;

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

    /**
     * @ORM\PostPersist()
     * @ORM\PostUpdate()
     */
    public function upload()
    
        if (null === $this->file) 
            return;
        

        // if there is an error when moving the file, an exception will
        // be automatically thrown by move(). This will properly prevent
        // the entity from being persisted to the database on error
        $this->file->move($this->getUploadRootDir(), $this->path);

        unset($this->file);
    

    /**
     * @ORM\PostRemove()
     */
    public function removeUpload()
    
        if ($file = $this->getAbsolutePath()) 
            unlink($file);
        
        

    public function getAbsolutePath()
    
        return null === $this->path ? null : $this->getUploadRootDir().'/'.$this->path;
    

    public function getWebPath()
    
        return null === $this->path ? null : $this->getUploadDir().'/'.$this->path;
    

    protected function getUploadRootDir()
    
        // the absolute directory path where uploaded documents should be saved
        return __DIR__.'/../../../../web/'.$this->getUploadDir();

    

    protected function getUploadDir()
    
        // get rid of the __DIR__ so it doesn't screw when displaying uploaded doc/image in the view.
        return 'uploads/images/'.$this->getCompany()->getId();
    


    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    
        return $this->id;
    

    /**
     * Set name
     *
     * @param string $name
     */
    public function setName($name)
    
        $this->name = $name;
    

    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    
        return $this->name;
    

    /**
     * Set width
     *
     * @param integer $width
     */
    public function setWidth($width)
    
        $this->width = $width;
    

    /**
     * Get width
     *
     * @return integer 
     */
    public function getWidth()
    
        return $this->width;
    

    /**
     * Set height
     *
     * @param integer $height
     */
    public function setHeight($height)
    
        $this->height = $height;
    

    /**
     * Get height
     *
     * @return integer 
     */
    public function getHeight()
    
        return $this->height;
    

    /**
     * Set path
     *
     * @param string $path
     */
    public function setPath($path)
    
        $this->path = $path;
    

    /**
     * Get path
     *
     * @return string 
     */
    public function getPath()
    
        return $this->path;
    

    /**
     * Set company
     *
     * @param BizTV\BackendBundle\Entity\company $company
     */
    public function setCompany(\BizTV\BackendBundle\Entity\company $company)
    
        $this->company = $company;
    

    /**
     * Get company
     *
     * @return BizTV\BackendBundle\Entity\company 
     */
    public function getCompany()
    
        return $this->company;
    

    /**
     * Set folder
     *
     * @param BizTV\MediaManagementBundle\Entity\Folder $folder
     */
    public function setFolder(\BizTV\MediaManagementBundle\Entity\Folder $folder = NULL)
    
        $this->folder = $folder;
    

    /**
     * Get folder
     *
     * @return BizTV\MediaManagementBundle\Entity\Folder 
     */
    public function getFolder()
    
        return $this->folder;
    


    /**
     * Set qr
     *
     * @param BizTV\MediaManagementBundle\Entity\QrImage $qr
     */
    public function setQr(\BizTV\MediaManagementBundle\Entity\QrImage $qr = null)
    
        $this->qr = $qr;
    

    /**
     * Get qr
     *
     * @return BizTV\MediaManagementBundle\Entity\QrImage 
     */
    public function getQr()
    
        return $this->qr;
    


    /**
     * Set deleteRequested
     *
     * @param date $deleteRequested
     */
    public function setDeleteRequested($deleteRequested = null)
    
        $this->deleteRequested = $deleteRequested;
    

    /**
     * Get deleteRequested
     *
     * @return date 
     */
    public function getDeleteRequested()
    
        return $this->deleteRequested;
    

【问题讨论】:

一点建议:避免像瘟疫那样混合形式和实体。编写额外的代码来自己处理。相信我,这 ^this^ 不会是他们最后一次给你带来麻烦。 我的做法完全出自 Symfony2 食谱...symfony.com/doc/2.3/cookbook/doctrine/file_uploads.html Flosculus 如何在控制器中处理文件上传? 你试过从你的控制器直接访问 $_FILES php superglobal 吗?这应该工作。 感觉就像破解 sym2,而不是我想要做的......我认为 =) 【参考方案1】:

我找到了我要找的东西。要访问从控制器上传到 symfony 的文件,你只需要这样做:

    $request = $this->getRequest();
    $file = $request->files->get('file1'); //file1 being the name of my form field for the file

    /* if your entity is set up like mine - like they teach you in the symfony2 cookbook
     * file is actually a public property so you can just set it like this
     **/
    $entity->file = $file; 

    //and here's how you get the original name of that file
    $entity->setName( $file->getClientOriginalName() );

【讨论】:

【参考方案2】:

首先,如果您想在表单提交/绑定/handleRequest 或其他其他操作之后获取文件的实体,您需要在表单配置方法中提供data_class 选项(setDefaultOptions 等)。只有在那之后,您的表单才会在提交后开始返回所需的实体。

【讨论】:

在实际使用表单类型的同步请求中,此设置没有问题,我唯一的问题是在使用带有自定义数据的 ajax 提交时获取控制器中上传的文件。 【参考方案3】:

1)首先创建你的实体:

namespace XXX;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Table()
 * @ORM\Entity
 */
class Article

    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

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

    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    
        return $this->id;
    

    /**
     * Set image
     *
     * @param string $image
     */
    public function setImage($image)
    
        $this->image = $image;
    

    /**
     * Get image
     *
     * @return string
     */
    public function getImage()
    
        return $this->image;
    


2) 构建您的表单:因此,我们将为该文章实体创建一个简单的表单类型,以适应其他表单:ArticleType.php

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class ArticleType extends AbstractType

    public function buildForm(FormBuilder $builder, array $options)
    
        $builder
            ->add('image')
            ->add('...')

        ;
    

    public function getName()
    
        return 'xxx_articletype';
    

3)创建控制器:下面的控制器展示了如何管理整个过程:ArticleController.php:

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

/**
 * Article controller.
 *
 */
class ArticleController extends Controller


    /**
     * Finds and displays a Article entity.
     *
     */
    public function showAction($id)
    

        $em = $this->getDoctrine()->getEntityManager();
        $entity = $em->getRepository('XXXBundle:Article')->find($id);
        if (!$entity) 
            throw $this->createNotFoundException('Unable to find Article entity.');
        
    return $this->render('XXXBundle:Article:show.html.twig', array(
            'entity'      => $entity,

        ));
    
    /**
     * Displays a form to create a new Article entity.
     *
     */
    public function newAction()
    
        $entity = new Article();
        //$entity = $em->getRepository('CliniqueGynecoBundle:Article');
        $form   = $this->createForm(new ArticleType(), $entity);

        return $this->render('XXXBundle:Article:new.html.twig', array(
            'entity' => $entity,
            'form'   => $form->createView()
        ));
    

    /**
     * Creates a new Article entity.
     *
     */
    public function createAction()
    
        $entity  = new Article();
        $request = $this->getRequest();
        $form    = $this->createForm(new ArticleType(), $entity);
        $form->bindRequest($request);

        if ($form->isValid()) 
            $em = $this->getDoctrine()->getEntityManager();
            $em->persist($entity);
            $em->flush();

            return $this->redirect($this->generateUrl('article_show', array('id' => $entity->getId())));

        

        return $this->render('XXXBundle:Article:new.html.twig', array(
            'entity' => $entity,
            'form'   => $form->createView()
        ));
    

    private function createDeleteForm($id)
    
        return $this->createFormBuilder(array('id' => $id))
            ->add('id', 'hidden')
            ->getForm()
        ;
    

4) 上传表单的布局:new.html.twig

<form action=" path('basearticle_create') " method="post"  form_enctype(form) >
     form_widget(form) 
    <p>
        <button class="btn btn-primary" type="submit">Create</button>
    </p>
</form>

5) 显示布局:show.html.twig

<table>
 <tr>
      <td align="center" valign="top"><img src=" asset('upload/' ~ entity.id ~'/' ~ entity.image)"    /></td>
</tr>
</table>

6) 在“生命周期回调”中使用“生命周期回调”挂钩实体:«@ ORM \ HasLifecycleCallbacks »

/**
 *
 * @ORM\Table()
 * @ORM\HasLifecycleCallbacks
 * @ORM\Entity
 */
class Article

....

7) 新增文件下载方法:

class Article

   ....................................

    public function getFullImagePath() 
        return null === $this->image ? null : $this->getUploadRootDir(). $this->image;
    

    protected function getUploadRootDir() 
        // the absolute directory path where uploaded documents should be saved
        return $this->getTmpUploadRootDir().$this->getId()."/";
    

    protected function getTmpUploadRootDir() 
        // the absolute directory path where uploaded documents should be saved
        return __DIR__ . '/../../../../web/upload/';
    

    /**
     * @ORM\PrePersist()
     * @ORM\PreUpdate()
     */
    public function uploadImage() 
        // the file property can be empty if the field is not required
        if (null === $this->image) 
            return;
        
        if(!$this->id)
            $this->image->move($this->getTmpUploadRootDir(), $this->image->getClientOriginalName());
        else
            $this->image->move($this->getUploadRootDir(), $this->image->getClientOriginalName());
        
        $this->setImage($this->image->getClientOriginalName());
    

    /**
     * @ORM\PostPersist()
     */
    public function moveImage()
    
        if (null === $this->image) 
            return;
        
        if(!is_dir($this->getUploadRootDir()))
            mkdir($this->getUploadRootDir());
        
        copy($this->getTmpUploadRootDir().$this->image, $this->getFullImagePath());
        unlink($this->getTmpUploadRootDir().$this->image);
    

    /**
     * @ORM\PreRemove()
     */
    public function removeImage()
    
        unlink($this->getFullImagePath());
        rmdir($this->getUploadRootDir());
    

【讨论】:

以上是关于如何使用 symfony2“手动”处理文件上传?的主要内容,如果未能解决你的问题,请参考以下文章

Dropzone 手动删除上传的文件

Symfony2 中多个文件上传的问题

Symfony2 限制最终用户仅上传 csv 文件

上传图片时如何在 symfony2 奏鸣曲媒体中设置最大上传大小

Symfony2:如何手动登录用户? [复制]

Symfony2文件上传获取原始文件名