如何使用 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“手动”处理文件上传?的主要内容,如果未能解决你的问题,请参考以下文章