首页 > 语言 > PHP > 正文

Doctrine文件上传处理例子

2024-09-04 11:45:12
字体:大 中 小
来源:转载
供稿:网友

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

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

  1. // src/Acme/DemoBundle/Entity/Document.php 
  2. namespace Acme/DemoBundle/Entity; 
  3.    
  4. use Doctrine/ORM/Mapping as ORM; 
  5. use Symfony/Component/Validator/Constraints as Assert; 
  6.    
  7. /** 
  8.  * @ORM/Entity 
  9.  */ 
  10. class Document 
  11. { 
  12.     /** 
  13.      * @ORM/Id 
  14.      * @ORM/Column(type="integer") 
  15.      * @ORM/GeneratedValue(strategy="AUTO") 
  16.      */ 
  17.     public $id; 
  18.    
  19.     /** 
  20.      * @ORM/Column(type="string", length=255) 
  21.      * @Assert/NotBlank 
  22.      */ 
  23.     public $name; 
  24.    
  25.     /** 
  26.      * @ORM/Column(type="string", length=255, nullable=true) 
  27.      */ 
  28.     public $path; 
  29.    
  30.     public function getAbsolutePath() 
  31.     { 
  32.         return null === $this->path 
  33.             ? null 
  34.             : $this->getUploadRootDir().'/'.$this->path; 
  35.     } 
  36.    
  37.     public function getWebPath() 
  38.     { 
  39.         return null === $this->path 
  40.             ? null 
  41.             : $this->getUploadDir().'/'.$this->path; 
  42.     } 
  43.    
  44.     protected function getUploadRootDir() 
  45.     { 
  46.         // the absolute directory path where uploaded 
  47.         // documents should be saved 
  48.         return __DIR__.'/../../../../web/'.$this->getUploadDir(); 
  49.     } 
  50.    
  51.     protected function getUploadDir() 
  52.     { 
  53.         // get rid of the __DIR__ so it doesn't screw up 
  54.         // when displaying uploaded doc/image in the view. 
  55.         return 'uploads/documents'; 
  56.     } 
  57. } 

该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,他可能是这样的.

  1. public function uploadAction() 
  2. { 
  3.     // ... 
  4.    
  5.     $form = $this->createFormBuilder($document) 
  6.         ->add('name') 
  7.         ->add('file') 
  8.         ->getForm(); 
  9.    
  10.     // ... 
  11. } 

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

  1. use Symfony/Component/HttpFoundation/File/UploadedFile; 
  2.    
  3. // ... 
  4. class Document 
  5. { 
  6.     /** 
  7.      * @Assert/File(maxSize="6000000") 
  8.      */ 
  9.     private $file; 
  10.    
  11.     /** 
  12.      * Sets file. 
  13.      * 
  14.      * @param UploadedFile $file 
  15.      */ 
  16.     public function setFile(UploadedFile $file = null) 
  17.     { 
  18.         $this->file = $file; 
  19.     } 
  20.    
  21.     /** 
  22.      * Get file. 
  23.      * 
  24.      * @return UploadedFile 
  25.      */ 
  26.     public function getFile() 
  27.     { 
  28.         return $this->file; 
  29.     } 
  30. } 
  31. annotations 
  32. Annotations 
  33. // src/Acme/DemoBundle/Entity/Document.php 
  34. namespace Acme/DemoBundle/Entity; 
  35.    
  36. // ... 
  37. use Symfony/Component/Validator/Constraints as Assert; 
  38.    
  39. class Document 
  40. { 
  41.     /** 
  42.      * @Assert/File(maxSize="6000000") 
  43.      */ 
  44.     private $file; 
  45.    
  46.     // ... 
  47. } 

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

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

  1. // ... 
  2. use Acme/DemoBundle/Entity/Document; 
  3. use Sensio/Bundle/FrameworkExtraBundle/Configuration/Template; 
  4. use Symfony/Component/HttpFoundation/Request; 
  5. // ... 
  6.    
  7. /** 
  8.  * @Template() 
  9.  */ 
  10. public function uploadAction(Request $request) 
  11. { 
  12.     $document = new Document(); 
  13.     $form = $this->createFormBuilder($document) 
  14.         ->add('name') 
  15.         ->add('file') 
  16.         ->getForm(); 
  17.    
  18.     $form->handleRequest($request); 
  19.    
  20.     if ($form->isValid()) { 
  21.         $em = $this->getDoctrine()->getManager(); 
  22.    
  23.         $em->persist($document); 
  24.         $em->flush(); 
  25.    
  26.         return $this->redirect($this->generateUrl(...)); 
  27.     } 
  28.    
  29.     return array('form' => $form->createView()); 
  30. } 

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

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

  1. if ($form->isValid()) { 
  2.     $em = $this->getDoctrine()->getManager(); 
  3.    
  4.     $document->upload(); 
  5.    
  6.     $em->persist($document); 
  7.     $em->flush(); 
  8.    
  9.     return $this->redirect(...); 
  10. } 

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

  1. public function upload() 
  2. { 
  3.     // the file property can be empty if the field is not required 
  4.     // 该file属性为空这个属性就不需要了 
  5.     if (null === $this->getFile()) { 
  6.         return; 
  7.     } 
  8.    
  9.     // use the original file name here but you should 
  10.     // sanitize it at least to avoid any security issues 
  11.     //  这里你应该使用原文件名但是应该至少审核它避免一些安全问题 
  12.     // move takes the target directory and then the 
  13.     // target filename to move to 
  14.     // 将目标文件移动到目标目录 
  15.     $this->getFile()->move( 
  16.         $this->getUploadRootDir(), 
  17.         $this->getFile()->getClientOriginalName() 
  18.     ); 
  19.    
  20.     // set the path property to the filename where you've saved the file 
  21.     // 设置path属性为你保存文件的文件名 
  22.     $this->path = $this->getFile()->getClientOriginalName(); 
  23.    
  24.     // clean up the file property as you won't need it anymore 
  25.     // 清理你不需要的file属性 
  26.     $this->file = null; 
  27. } 

使用生命周期回调:

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

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

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

  1. /** 
  2.  * @ORM/Entity 
  3.  * @ORM/HasLifecycleCallbacks 
  4.  */ 
  5. class Document 
  6. { 
  7. } 

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

  1. use Symfony/Component/HttpFoundation/File/UploadedFile; 
  2.    
  3. /** 
  4.  * @ORM/Entity 
  5.  * @ORM/HasLifecycleCallbacks 
  6.  */ 
  7. class Document 
  8. { 
  9.     private $temp; 
  10.    
  11.     /** 
  12.      * Sets file. 
  13.      * 
  14.      * @param UploadedFile $file 
  15.      */ 
  16.     public function setFile(UploadedFile $file = null) 
  17.     { 
  18.         $this->file = $file; 
  19.         // check if we have an old image path 
  20.         // 检查如果我们有一个旧的图片路径 
  21.         if (isset($this->path)) { 
  22.             // store the old name to delete after the update 
  23.             $this->temp = $this->path; 
  24.             $this->path = null; 
  25.         } else { 
  26.             $this->path = 'initial'; 
  27.         } 
  28.     } 
  29.    
  30.     /** 
  31.      * @ORM/PrePersist() 
  32.      * @ORM/PreUpdate() 
  33.      */ 
  34.     public function preUpload() 
  35.     { 
  36.         if (null !== $this->getFile()) { 
  37.             // do whatever you want to generate a unique name 
  38.             // 去生成一个唯一的名称 
  39.             $filename = sha1(uniqid(mt_rand(), true)); 
  40.             $this->path = $filename.'.'.$this->getFile()->guessExtension(); 
  41.         } 
  42.     } 
  43.    
  44.     /** 
  45.      * @ORM/PostPersist() 
  46.      * @ORM/PostUpdate() 
  47.      */ 
  48.     public function upload() 
  49.     { 
  50.         if (null === $this->getFile()) { 
  51.             return; 
  52.         } 
  53.    
  54.         // if there is an error when moving the file, an exception will 
  55.         // be automatically thrown by move(). This will properly prevent 
  56.         // the entity from being persisted to the database on error 
  57.         //当移动文件发生错误,一个异常move()会自动抛出异常。 
  58.         //这将阻止实体持久化数据库发生错误。 
  59.         $this->getFile()->move($this->getUploadRootDir(), $this->path); 
  60.    
  61.         // check if we have an old image 
  62.         if (isset($this->temp)) { 
  63.             // delete the old image 
  64.             unlink($this->getUploadRootDir().'/'.$this->temp); 
  65.             // clear the temp image path 
  66.             $this->temp = null; 
  67.         } 
  68.         $this->file = null; 
  69.     } 
  70.    
  71.     /** 
  72.      * @ORM/PostRemove() 
  73.      */ 
  74.     public function removeUpload() 
  75.     { 
  76.         $file = $this->getAbsolutePath(); 
  77.         if ($file) { 
  78.             unlink($file); 
  79.         } 
  80.     } 
  81. } 

如果更改你的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中移除了:

  1. if ($form->isValid()) { 
  2.     $em = $this->getDoctrine()->getManager(); 
  3.    
  4.     $em->persist($document); 
  5.     $em->flush(); 
  6.    
  7.     return $this->redirect(...); 
  8. } 

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

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

使用ID作为文件名

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

  1. use Symfony/Component/HttpFoundation/File/UploadedFile; 
  2.    
  3. /** 
  4.  * @ORM/Entity 
  5.  * @ORM/HasLifecycleCallbacks 
  6.  */ 
  7. class Document 
  8. { 
  9.     private $temp; 
  10.    
  11.     /** 
  12.      * Sets file. 
  13.      * 
  14.      * @param UploadedFile $file 
  15.      */ 
  16.     public function setFile(UploadedFile $file = null) 
  17.     { 
  18.         $this->file = $file; 
  19.         // check if we have an old image path 
  20.         if (is_file($this->getAbsolutePath())) { 
  21.             // store the old name to delete after the update 
  22.             $this->temp = $this->getAbsolutePath(); 
  23.         } else { 
  24.             $this->path = 'initial'; 
  25.         } 
  26.     } 
  27.    
  28.     /** 
  29.      * @ORM/PrePersist() 
  30.      * @ORM/PreUpdate() 
  31.      */ 
  32.     public function preUpload() 
  33.     { 
  34.         if (null !== $this->getFile()) { 
  35.             $this->path = $this->getFile()->guessExtension(); 
  36.         } 
  37.     } 
  38.    
  39.     /** 
  40.      * @ORM/PostPersist() 
  41.      * @ORM/PostUpdate() 
  42.      */ 
  43.     public function upload() 
  44.     { 
  45.         if (null === $this->getFile()) { 
  46.             return; 
  47.         } 
  48.    
  49.         // check if we have an old image 
  50.         if (isset($this->temp)) { 
  51.             // delete the old image 
  52.             unlink($this->temp); 
  53.             // clear the temp image path 
  54.             $this->temp = null; 
  55.         } 
  56.    
  57.         // you must throw an exception here if the file cannot be moved 
  58.         // so that the entity is not persisted to the database 
  59.         // which the UploadedFile move() method does 
  60.         $this->getFile()->move( 
  61.             $this->getUploadRootDir(), 
  62.             $this->id.'.'.$this->getFile()->guessExtension() 
  63.         ); 
  64.    
  65.         $this->setFile(null); 
  66.     } 
  67.    
  68.     /** 
  69.      * @ORM/PreRemove() 
  70.      */ 
  71.     public function storeFilenameForRemove() 
  72.     { 
  73.         $this->temp = $this->getAbsolutePath(); 
  74.     } 
  75.    
  76.     /** 
  77.      * @ORM/PostRemove() 
  78.      */ 
  79.     public function removeUpload() 
  80.     {  //Vevb.com 
  81.         if (isset($this->temp)) { 
  82.             unlink($this->temp); 
  83.         } 
  84.     } 
  85.    
  86.     public function getAbsolutePath() 
  87.     { 
  88.         return null === $this->path 
  89.             ? null 
  90.             : $this->getUploadRootDir().'/'.$this->id.'.'.$this->path; 
  91.     } 
  92. } 

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

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表