Symfony 2文件上传无报错未生效问题求助(附代码)
我之前也碰到过Symfony文件上传这种看似无报错但完全没效果的坑,大概率是几个容易忽略的细节没做好,咱们一步步来排查修复:
1. Doctrine生命周期方法未被正确触发(最关键)
你在Entity里写的__persist()、__postPersist()这些自定义方法,Doctrine根本不会自动调用!得用Doctrine的生命周期注解来标记这些方法,ORM才会在对应的阶段执行它们。
修复后的Entity相关代码:
// 先确保导入了必要的注解 use Doctrine\ORM\Mapping as ORM; use Symfony\Component\HttpFoundation\File\File; // ... 其他实体代码 ... /** * @ORM\PrePersist */ public function prePersist() { if($this->file !== null) { $this->support = sha1(uniqid(mt_rand(), true)).'.'.$this->file->guessExtension(); } } /** * @ORM\PostPersist */ public function postPersist() { if($this->file === null) return; $this->file->move($this->getUploadRootDir(), $this->support); unset($this->file); $this->file = null; } /** * @ORM\PreUpdate */ public function preUpdate() { if($this->file === null) return; $this->preRemove(); $this->prePersist(); $this->postPersist(); } /** * @ORM\PreRemove */ public function preRemove() { if($file = $this->getAbsolutePath()) { if(file_exists($file)) { unlink($file); } } }
注意把方法名改成规范的prePersist、postPersist等,并且加上对应的@ORM\PrePersist这类注解,Doctrine才能识别并执行。
2. Twig模板中表单变量混淆
你的模板里同时混用了form和formUpload两个变量,这会导致表单渲染错误,文件字段根本没被正确提交。统一用formUpload(因为Controller里你创建的表单对象是$formUpload):
修复后的Twig代码:
<div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="exampleModalLongTitle">Upload facture</h5> </div> <div class="modal-body"> {{ form_start(formUpload, {'attr': {'novalidate': 'novalidate', 'data-parsley-validate':'data-parsley-validate'}}) }} {{ form_row(formUpload.file) }} </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-dismiss="modal">Fermer</button> {{ form_row(formUpload.save) }} {{ form_end(formUpload) }} </div> </div>
另外,Symfony的form_start会自动添加enctype="multipart/form-data"属性(只要表单里有FileType字段),这是文件上传必须的,不用手动添加,但要确保你的Form类型里正确配置了File字段。
3. 表单类型(Form Type)的关键配置缺失
你没提供Form类型的代码,但必须确保你的invoicesupport表单类型里,file字段是用FileType(适配你用的Symfony2/3版本):
创建对应的Form类(比如InvoiceSupportType.php):
<?php namespace Main\ConfigBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\Extension\Core\Type\FileType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; class InvoiceSupportType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('file', FileType::class, [ 'label' => 'Fichier de support', 'required' => true, // 根据需求设置是否必填 'mapped' => true, // 关联到Entity的file字段 ]) ->add('save', SubmitType::class, [ 'label' => 'Enregistrer' ]); } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => 'Main\ConfigBundle\Entity\InvoiceCoach', ]); } }
然后在Controller里确保你正确注册了这个表单类型(或者用别名invoicesupport对应这个类)。
4. Controller逻辑的小问题
你的Controller里,不管表单是否有效,都会执行重定向,这样如果表单有错误(比如文件大小超限、格式不对),你根本看不到错误信息,直接跳走了。把重定向放到if($formUpload->isValid())的块里面:
修复后的Controller代码:
$em = $this->getDoctrine()->getManager(); $invoice = $em->getRepository("MainConfigBundle:InvoiceCoach")->find($id); $formUpload = $this->createForm('invoicesupport', $invoice) ->add('save', 'submit', array('label' => 'Enregistrer') ); if($request->isMethod("POST")){ $formUpload->handleRequest($request); if($formUpload->isValid()){ $em->persist($invoice); $em->flush(); // 只有表单有效才重定向 return $this->redirect($this->generateUrl('coach_paid_edit', array('id' => $id) )); } } // 记得添加渲染模板的代码,比如: // return $this->render('MainConfigBundle:Invoice:upload.html.twig', [ // 'formUpload' => $formUpload->createView(), // 'id' => $id // ]);
另外,确保你在Controller里返回了渲染模板的响应,不然可能导致页面空白或者跳转异常。
5. 最后检查上传目录权限
确保C:\wamp64\www\myproject\web\upload目录有写入权限,WAMP下通常需要给IUSR或者www-data用户(取决于你的服务器配置)赋予读写权限,不然文件上传会失败但不会抛出明显的错误。
按照这些步骤修复后,应该就能正常上传文件到目标目录,并且数据库的support字段会正确存储文件名了。
内容的提问来源于stack exchange,提问作者Antoine Bernard

