好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Doctrine文件上传处理例子_php上传教程

Doctrine是基于数据库抽像层上的ORM,它可以通过PHP对象轻松访问所有的数据库,例如MYSQL,它支持的PHP最低版本为5.2.3,下面我们一起来看看Doctrine文件上传处理例子,希望文章对各位有帮助.

基本设置,创建一个简单的Doctrine实体类:

// src/Acme/DemoBundle/Entity/Document.php   namespace Acme\DemoBundle\Entity;      use  Doctrine\ORM\Mapping  as  ORM;  use  Symfony\Component\Validator\Constraints  as  Assert;      /**    * @ORM\Entity    */   class  Document  {       /**        * @ORM\Id        * @ORM\Column(type="integer")        * @ORM\GeneratedValue(strategy="AUTO")        */        public   $id ;           /**        * @ORM\Column(type="string", length=255)        * @Assert\NotBlank        */        public   $name ;           /**        * @ORM\Column(type="string", length=255, nullable=true)        */        public   $path ;           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 up            // when displaying uploaded doc/image in the view.            return   'uploads/documents' ;      }  } 

该document实体有一个名称与文件相关联,这个path属性存储一个文件的相对路径并且在数据库中存储,这个getAbsolutePath()会返回一个绝对路径,getWebPath()会返回一个web路径,用于模板加入上传文件链接.

如果你还没有这样做的话,你应该阅读http://symfony.com/doc/current/reference/forms/types/file.html首先了解基本的上传过程.

如果您使用注释来验证规则(如本例所示),请确保你启用了注释验证(见http://symfony.com/doc/current/book/validation.html#book-validation-configuration).

在处理一个实际的文件上传时,使用一个[虚拟]的file字段,例如,如果你在controller中直接构建一个form,他可能是这样的.

public   function  uploadAction()  {       // ...            $form  =  $this ->createFormBuilder( $document )          ->add( 'name' )          ->add( 'file' )          ->getForm();           // ...   } 

下一步,创建file这个属性到你的Document类中并且添加一些验证规则:

use  Symfony\Component\HttpFoundation\File\UploadedFile;      // ...   class  Document  {       /**        * @Assert\File(maxSize="6000000")        */        private   $file ;           /**        * Sets file.        *        * @param UploadedFile $file        */        public   function  setFile(UploadedFile  $file  = null)      {           $this ->file =  $file ;      }           /**        * Get file.        *        * @return UploadedFile        */        public   function  getFile()      {           return   $this ->file;      }  }  annotations  Annotations  // src/Acme/DemoBundle/Entity/Document.php   namespace Acme\DemoBundle\Entity;      // ...   use  Symfony\Component\Validator\Constraints  as  Assert;      class  Document  {       /**        * @Assert\File(maxSize="6000000")        */        private   $file ;           // ...   } 

当你使用File约束,symfony会自动猜测表单字段输入的是一个文件上传,这就是当你创建表单(->add(‘file’))时,为什么没有在表单明确设置为文件上传的原因.

下面的控制器,告诉您如何处理全部过程:

// ...   use  Acme\DemoBundle\Entity\Document;  use  Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;  use  Symfony\Component\HttpFoundation\Request;  // ...       /**    * @Template()    */   public   function  uploadAction(Request  $request )  {       $document  =  new  Document();       $form  =  $this ->createFormBuilder( $document )          ->add( 'name' )          ->add( 'file' )          ->getForm();           $form ->handleRequest( $request );           if  ( $form ->isValid()) {           $em  =  $this ->getDoctrine()->getManager();               $em ->persist( $document );           $em -> flush ();               return   $this ->redirect( $this ->generateUrl(...));      }           return   array ( 'form'  =>  $form ->createView());  } 

以前的controller当提交name自动的存储Document实体,但是他不会做任何关于文件的事情并且path属性也将是空白.

处理文件上传一个简单的方法就是在entity持久化之前设置相应的path属性,在某一时刻处理文件上传时,要调用Document实体类一个upload()方法给path赋值.

if  ( $form ->isValid()) {       $em  =  $this ->getDoctrine()->getManager();           $document ->upload();           $em ->persist( $document );       $em -> flush ();           return   $this ->redirect(...);  } 

这个upload()方法利用UploadedFile对象,是它提交后返回file字段:

public   function  upload()  {       // the file property can be empty if the field is not required        // 该file属性为空这个属性就不需要了        if  (null ===  $this ->getFile()) {           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 ->getFile()->move(           $this ->getUploadRootDir(),           $this ->getFile()->getClientOriginalName()      );           // set the path property to the filename where you've saved the file        // 设置path属性为你保存文件的文件名        $this ->path =  $this ->getFile()->getClientOriginalName();           // clean up the file property as you won't need it anymore        // 清理你不需要的file属性        $this ->file = null;  } 

使用生命周期回调:

生命周期回调是一种有限的技术,他有一些缺点,如果你想移除Document::getUploadRootDir()方法里的写死的编码__DIR__,最好的方法是开始使用Doctrine listeners,在哪里你将能够注入内核参数,如kernel.root_dir来建立绝对路径.

这种原理工作,他有一个缺陷:也就是说当entity持久化时会有什么问题呢?答:该文件已经转移到了它的最终位置,实体类下的path属性不能够正确的实体化.

如果entity有持久化问题或者文件不能够移动,什么事情也没有发生,为了避免这些问题,你应该改变这种实现方式以便数据库操作和自动删除文件:

/**    * @ORM\Entity    * @ORM\HasLifecycleCallbacks    */   class  Document  {  } 

接下来,利用这些回调函数重构Document类:

use  Symfony\Component\HttpFoundation\File\UploadedFile;      /**    * @ORM\Entity    * @ORM\HasLifecycleCallbacks    */   class  Document  {       private   $temp ;           /**        * Sets file.        *        * @param UploadedFile $file        */        public   function  setFile(UploadedFile  $file  = null)      {           $this ->file =  $file ;           // check if we have an old image path            // 检查如果我们有一个旧的图片路径            if  (isset( $this ->path)) {               // store the old name to delete after the update                $this ->temp =  $this ->path;               $this ->path = null;          }  else  {               $this ->path =  'initial' ;          }      }           /**        * @ORM\PrePersist()        * @ORM\PreUpdate()        */        public   function  preUpload()      {           if  (null !==  $this ->getFile()) {               // do whatever you want to generate a unique name                // 去生成一个唯一的名称                $filename  = sha1(uniqid(mt_rand(), true));               $this ->path =  $filename . '.' . $this ->getFile()->guessExtension();          }      }           /**        * @ORM\PostPersist()        * @ORM\PostUpdate()        */        public   function  upload()      {           if  (null ===  $this ->getFile()) {               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            //当移动文件发生错误,一个异常move()会自动抛出异常。            //这将阻止实体持久化数据库发生错误。            $this ->getFile()->move( $this ->getUploadRootDir(),  $this ->path);               // check if we have an old image            if  (isset( $this ->temp)) {               // delete the old image               unlink( $this ->getUploadRootDir(). '/' . $this ->temp);               // clear the temp image path                $this ->temp = null;          }           $this ->file = null;      }           /**        * @ORM\PostRemove()        */        public   function  removeUpload()      {           $file  =  $this ->getAbsolutePath();           if  ( $file ) {              unlink( $file );          }      }  } 

如果更改你的entity是由Doctrine event listener 或event subscriber处理,这个 preUpdate()回调函数必须通知Doctrine关于正在做的改变,有关preUpdate事件限制的完整参考请查看 http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#preupdate

现在这个类做了你需要的一切:他会在entity持久化之前生成一个唯一的文件名,持久化之后,移动文件,删除文件.

现在移动文件是entity自动完成,这个$document->upload()就应该从controller中移除了:

if  ( $form ->isValid()) {       $em  =  $this ->getDoctrine()->getManager();           $em ->persist( $document );       $em -> flush ();           return   $this ->redirect(...);  } 

这个@ORM\PrePersist()和@ORM\PostPersist()事件回调:一个是在entity持久化到数据库之前触发,一个是在entity持久化到数据库之后触发。另一方面, @ORM\PreUpdate() 和 @ORM\PostUpdate()事件回调时当实体更新时触发。

当改变entity字段后进行持久化操作时,PreUpdate和PostUpdate回调才会被触发。这意味着,默认情况下,你只改变了$file属性,这些事件不会被触发,因为这个属性它自己不会持久化到Doctrine。有一个解决方法,就是创建一个updated字段把它持久化到Doctrine,并当文件改变时手动调整它。

使用ID作为文件名

如果要使用ID作为文件名,实现略有不同,您需要保存path属性为文件扩展名,而不是实际的文件名:

use  Symfony\Component\HttpFoundation\File\UploadedFile;      /**    * @ORM\Entity    * @ORM\HasLifecycleCallbacks    */   class  Document  {       private   $temp ;           /**        * Sets file.        *        * @param UploadedFile $file        */        public   function  setFile(UploadedFile  $file  = null)      {           $this ->file =  $file ;           // check if we have an old image path            if  ( is_file ( $this ->getAbsolutePath())) {               // store the old name to delete after the update                $this ->temp =  $this ->getAbsolutePath();          }  else  {               $this ->path =  'initial' ;          }      }           /**        * @ORM\PrePersist()        * @ORM\PreUpdate()        */        public   function  preUpload()      {           if  (null !==  $this ->getFile()) {               $this ->path =  $this ->getFile()->guessExtension();          }      }           /**        * @ORM\PostPersist()        * @ORM\PostUpdate()        */        public   function  upload()      {           if  (null ===  $this ->getFile()) {               return ;          }               // check if we have an old image            if  (isset( $this ->temp)) {               // delete the old image               unlink( $this ->temp);               // clear the temp image path                $this ->temp = null;          }               // you must throw an exception here if the file cannot be moved            // so that the entity is not persisted to the database            // which the UploadedFile move() method does            $this ->getFile()->move(               $this ->getUploadRootDir(),               $this ->id. '.' . $this ->getFile()->guessExtension()          );               $this ->setFile(null);      }           /**        * @ORM\PreRemove()        */        public   function  storeFilenameForRemove()      {           $this ->temp =  $this ->getAbsolutePath();      }           /**        * @ORM\PostRemove()        */        public   function  removeUpload()      {   //phpfensi.com            if  (isset( $this ->temp)) {              unlink( $this ->temp);          }      }           public   function  getAbsolutePath()      {           return  null ===  $this ->path              ? null              :  $this ->getUploadRootDir(). '/' . $this ->id. '.' . $this ->path;      }  } 

你会注意到,在这种情况下,你需要做一点工作,以删除该文件,在数据删除之前,你必须保存文件路径(因为它依赖于ID),然后,一旦对象已经完全从数据库中删除,你就可以安全的删除文件(在数据删除之后).

 


查看更多关于Doctrine文件上传处理例子_php上传教程的详细内容...

  阅读:68次