使用Symfony2上传多个文件

时间:2011-07-18 17:40:44

标签: file-upload symfony

我正在尝试通过表单上传多个文件,但我一次只能上传一个文件,这是我在浏览器中标记的最后一个文件。有没有办法使用简单的表单使用Symfony2上传更多图像? 这是我用来标记多个文件的表格的树枝模板:

{{ form_widget(form.post_image, { 'attr': {'multiple': 'multiple' }}) }} 

11 个答案:

答案 0 :(得分:12)

Ok解决绑定问题(enctype语法错误):我会给你我使用的代码。也许它会有所帮助...

我有图库实体

class Gallery
{
    protected $id;
    protected $name;
    protected $description;
    private $urlName;
    public $files; // the array which will contain the array of Uploadedfiles

    // GETTERS & SETTERS ...

    public function getFiles() {
        return $this->files;
    }
    public function setFiles(array $files) {
        $this->files = $files;
    }

    public function __construct() {
        $files = array();
    }
}

我有一个生成表单

表单
class Create extends AbstractType {

    public function buildForm(FormBuilder $builder, array $options) {

        $builder->add('name','text',array(
            "label" => "Name",
            "required" => TRUE,
        ));
        $builder->add('description','textarea',array(
            "label" => "Description",
            "required" => FALSE,
        ));
        $builder->add('files','file',array(
            "label" => "Fichiers",
            "required" => FALSE,
            "attr" => array(
                "accept" => "image/*",
                "multiple" => "multiple",
            )
        ));
    }
}

现在位于控制器

class GalleryController extends Controller
{
    public function createAction() {
        $gallery = new Gallery();
        $form = $this->createForm(new Create(), $gallery);
        // Altering the input field name attribute
        $formView = $form->createView();
        $formView->getChild('files')->set('full_name', 'create[files][]');

        $request = $this->getRequest();
        if($request->getMethod() == "POST")
        {
            $form->bindRequest($request);

            // print "<pre>".print_r($gallery->getFiles(),1)."</pre>";
            if($form->isValid())
            {
                // Do what you want with your files
                $this->get('gallery_manager')->save($gallery);
                return $this->redirect($this->generateUrl("_gallery_overview"));
            }
        }

        return $this->render("GalleryBundle:Admin:create.html.twig", array("form" => $formView));
    }
}

希望这有帮助...

注意:如果有人知道改变这个f * * 名称属性的更好方法,可能在FormView类中或通过声明新的字段类型,请随时向我们展示您的方法......

答案 1 :(得分:12)

不需要额外的课程(除了gallery_manger服务,但你描述的问题发生在......之前)

我真的不知道出了什么问题。检查您的模板(可能是错误的enctype ...或名称attr mismatching)

首先尝试单个文件上传,查看文档:

  1. file Field Type
  2. How to handle File Uploads with Doctrine
  3. 一旦有效,你必须编辑一些行。

    1. 添加输入文件的多个属性。
    2. 在输入文件名属性的末尾附加[](我的是 创建... []因为我的表单类名是create,如果是的话 createType它将是createType ... [])
    3. 将init $文件作为数组。
    4. 将代码复制/粘贴到此处。

答案 2 :(得分:7)

我在这里找到的所有建议都是针对实际情况的解决方法。

为了能够拥有多个附件,您应该使用表单集合。

来自文档的引用:

  

在此条目中,您将学习如何创建嵌入许多其他表单集合的表单。这可能很有用,例如,如果您有一个Task类,并且想要在同一个表单内编辑/创建/删除与该任务相关的许多Tag对象。

     

http://symfony.com/doc/2.0/cookbook/form/form_collections.html

示例案例:您有一个文档,该文档由DocumentType指定。该文档必须具有多个附件,您可以通过定义AttachmentType表单并将其作为集合添加到DocumentType表单来实现。

答案 3 :(得分:4)

对于sf&gt; 2.2: 在表单类型类中,添加此重写方法:

public function finishView(FormView $view, FormInterface $form, array $options) {
    $view->vars['form']->children['files']->vars['full_name'] .= '[]';
}

答案 4 :(得分:3)

使用此方法:

$form = $this->createFormBuilder()
->add('attachments','file', array('required' => true,"attr" => array(
  "multiple" => "multiple",
  )))
->add('save', 'submit', array(
  'attr' => array('class' => 'btn btn-primary btn-block btn-lg'),
  'label' => 'save'
  ))
->getForm();

然后通过jQuery将[]添加到输入的名称:

<input id="form_attachments" name="form[attachments]" required="required" multiple="multiple" type="file">

jQuery代码:

 <script>
  $(document).ready(function() {
    $('#form_attachments').attr('name',$('#form_attachments').attr('name')+"[]");
  });
</script>

答案 5 :(得分:2)

您需要更改需要映射数组的输入文件名属性。

<input type="file" name="name[]" multiple />

答案 6 :(得分:2)

请注意,我尝试使用以下语法在sf2中执行相同的操作:

在控制器中:

public function stuffAction() {

$form = $this->createFormBuilder()
                ->add('files','file',array(
                    "attr" => array(
                        "accept" => "image/*",
                        "multiple" => "multiple",
                    )
                ))
                ->getForm();

        $formView = $form->createView();
        $formView->getChild('files')->set('full_name', 'form[files][]');

// name param (eg 'form[files][]') need to be the generated name followed by []
// try doing this : $formView->getChild('files')->get('full_name') . '[]'


        $request = $this->getRequest();
        if($request->getMethod() == "POST") {

            $form->bindRequest($request);

            $data = $form->getData();
            $files = $data["files"];

            // do stuff with your files
        }

    }

    return $this->render('Bundle:Dir:index.html.twig',array("form" => $formView));
}

$files将是一系列已上传的文件......

调用$form->createView()来改变name属性当然不是最好的方法/最干净的方法,但它是我发现的唯一一个保持csrf功能正常工作的方法,因为改变了树枝模板中的name属性使得它无效......

现在我仍然有一个问题,使用生成表单的表单类,我不知道为什么在绑定表单数据和&amp;附加到表单的对象我的上传文件数组转换为(文件)名称???

的数组

答案 7 :(得分:2)

以上是上传多个文件的简单示例。我上传文件有类似的问题。

https://github.com/marekz/example_symfony_multiply_files_example

对于symfony 3。*

首先:两者都形成声明:

    <?php

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use AppBundle\Form\FilesType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;

class UserType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
                ->add('name')
                ->add('lastName')
                ->add('files', CollectionType::class,array(
                    'entry_type' => FilesType::class,
                    'allow_add' => true,
                    'by_reference' => false,
                ))
                ;
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\User'
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_user';
    }


}


    <?php

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class FilesType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('file');
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\Files'
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_files';
    }


}

现在,我的实体:

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
 */
class User {

    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255)
     */
    private $name;

    /**
     * @var string
     *
     * @ORM\Column(name="lastName", type="string", length=255)
     */
    private $lastName;

    /**
     * @ORM\ManyToMany(targetEntity="Files", cascade={"persist"})
     */
    private $files;

    function __construct() {
        $this->files = new ArrayCollection();
    }

    /**
     * Get id
     *
     * @return int
     */
    public function getId() {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     *
     * @return User
     */
    public function setName($name) {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string
     */
    public function getName() {
        return $this->name;
    }

    /**
     * Set lastName
     *
     * @param string $lastName
     *
     * @return User
     */
    public function setLastName($lastName) {
        $this->lastName = $lastName;

        return $this;
    }

    /**
     * Get lastName
     *
     * @return string
     */
    public function getLastName() {
        return $this->lastName;
    }

    /**
     * Get files
     * 
     * @return ArrayCollection
     */
    function getFiles() {
        return $this->files;
    }

    /**
     * Set files
     * @param type $files
     */
    function setFiles($files) {
        $this->files = $files;
    }
}

    <?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Files
 *
 * @ORM\Table(name="files")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\FilesRepository")
 */
class Files
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="file", type="string", length=255, unique=true)
     * @Assert\NotBlank(message="Please, upload the product brochure as a PDF file.")
     * @Assert\File(mimeTypes={ "application/pdf" })
     */
    private $file;

    /**
     *
     * @return Files
     */
    function getUser() {
        return $this->user;
    }

    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set file
     *
     * @param string $file
     *
     * @return Files
     */
    public function setFile($file)
    {
        $this->file = $file;

        return $this;
    }

    /**
     * Get file
     *
     * @return string
     */
    public function getFile()
    {
        return $this->file;
    }
    }

Finaly,Symfony Controller:

/**
 * Creates a new user entity.
 *
 * @Route("/new", name="user_new")
 * @Method({"GET", "POST"})
 */
public function newAction(Request $request) {
    $user = new User();
    $form = $this->createForm('AppBundle\Form\UserType', $user);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {

        $attachments = $user->getFiles();

        if ($attachments) {
            foreach($attachments as $attachment)
            {
                $file = $attachment->getFile();

                var_dump($attachment);
                $filename = md5(uniqid()) . '.' .$file->guessExtension();

                $file->move(
                        $this->getParameter('upload_path'), $filename
                );
                var_dump($filename);
                $attachment->setFile($filename);
            }
        }

        $em = $this->getDoctrine()->getManager();
        $em->persist($user);
        $em->flush();

        return $this->redirectToRoute('user_show', array('id' => $user->getId()));
    }

    return $this->render('user/new.html.twig', array(
                'user' => $user,
                'form' => $form->createView(),
    ));
}

答案 8 :(得分:1)

2.3中删除了getChildset()方法。 除此之外,您应该使用children[]vars属性

之前:

$formView->getChild('files')->set('full_name', 'form[files][]');

后:

$formView->children['files']->vars = array_replace($formView->children['files']->vars, array('full_name', 'form[files][]'));

答案 9 :(得分:1)

Symfony在symfony 2.5中为文件字段类型引入了'multiple'选项

$builder->add('file', 'file', array('multiple' => TRUE));

答案 10 :(得分:0)

如果会出现一些验证错误会怎样?将Symfony表重新发布多个文件上传字段。因为我尝试了它,我认为为此目的,您需要使用文件字段的集合。比symfony表单必须正确渲染所有字段。