Symfony 6.1 多文件上传需在实体中定义 array 类型字段、表单中配置 FileType 并设 'multiple' => true 和 'mapped' => false、控制器中手动处理 UploadedFile 数组并保存路径、模板使用 enctype="multipart/form-data"。

<p>在 Symfony 6.1 中实现多文件上传,核心是使用 <code>FileType</code> 字段并设置 <code>'multiple' => true</code>,配合实体字段类型为 <code>array</code>(或 <code>Collection</code>),并在控制器中正确处理上传逻辑。</p>
<H3>1. 实体中定义文件字段(推荐用 array 类型)</H3>
<p>不要用 <code>UploadedFile</code> 直接作为属性类型,应声明为 <code>array</code>,便于接收多个上传对象:</p>
<font color="#888">
<pre>
// src/Entity/Post.php
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Post
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
// 存储文件路径(上传后保存,非 UploadedFile)
#[ORM\Column(type: 'json', nullable: true)]
private array $imagePaths = [];
// getter/setter 略,注意 setImagePaths 接收 array,getPaths 返回 array
}
</pre>
</font>
<H3>2. 表单类型中配置 multiple 文件字段</H3>
<p>关键点:设 <code>'multiple' => true</code>、<code>'mapped' => false</code>(因 UploadedFile 不映射到实体属性),并添加 <code>'by_reference' => false</code>(若用 Collection):</p>
<font color="#888">
<pre>
// src/Form/PostType.php
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PostType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('images', FileType::class, [
'label' => '图片(可多选)',
'multiple' => true,
'mapped' => false, // 不自动映射到实体属性
'required' => false,
'attr' => ['accept' => 'image/*'],
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Post::class,
]);
}
}
</pre>
</font>
<H3>3. 控制器中手动处理多文件上传</H3>
<p>在 <code>handleRequest()</code> 后,从表单获取 <code>UploadedFile[]</code> 数组,逐个移动并保存路径:</p>
<font color="#888">
<pre>
// src/Controller/PostController.php
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\String\Slugger\SluggerInterface;
class PostController extends AbstractController
{
public function new(Request $request, SluggerInterface $slugger): Response
{
$post = new Post();
$form = $this->createForm(PostType::class, $post);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** @var UploadedFile[] $images */
$images = $form->get('images')->getData();
$imagePaths = [];
$uploadDir = $this->getParameter('kernel.project_dir').'/public/uploads';
foreach ($images as $file) {
$originalName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
$safeName = $slugger->slug($originalName);
$fileName = $safeName.'-'.uniqid().'.'.$file->guessExtension();
try {
$file->move($uploadDir, $fileName);
$imagePaths[] = '/uploads/'.$fileName;
} catch (\Exception $e) {
$this->addFlash('error', '文件上传失败:'.$e->getMessage());
return $this->render('post/new.html.twig', ['form' => $form]);
}
}
$post->setImagePaths($imagePaths);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($post);
$entityManager->flush();
$this->addFlash('success', '上传成功!');
return $this->redirectToRoute('post_index');
}
return $this->render('post/new.html.twig', ['form' => $form]);
}
}
</pre>
</font>
<H3>4. 模板中确保 form_enctype 正确</H3>
<p>Twig 模板必须使用 <code>enctype="multipart/form-data"</code>,且文件 input 要支持 multiple:</p>
<font color="#888">
<pre>
{# templates/post/new.html.twig #}
{{ form_start(form, {'attr': {'enctype': 'multipart/form-data'}}) }}
{{ form_row(form.images) }}
<button type="submit">提交</button>
{{ form_end(form) }}
</pre>
</font>
<p>不复杂但容易忽略:确保 <code>php.ini</code> 中 <code>max_file_uploads</code> 和 <code>post_max_size</code> 足够大(如 20 和 20M),否则多文件会静默失败。</p>