Symfony 2.3中使用Dropzone进行多文件上传

10

我尝试了一个新的JQuery库实现:dropzone.js

这个库提供了一个很好的多文件上传界面和拖放解决方案。

Symfony的多文件上传系统似乎不太容易。

我得到了这个错误:

Error: Cannot use object of type Symfony\Component\HttpFoundation\File\UploadedFile as array in /vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php line 87

我的实体文件:

/**
* @ORM\Entity
* @ORM\Table(name="media__file")
* @ORM\HasLifecycleCallbacks
*/
class File
{
/**
 * @ORM\Id
 * @ORM\Column(type="integer")
 * @ORM\GeneratedValue(strategy="AUTO")
 */
public $id;

/**
 * @ORM\Column(type="string", length=255)
 * @Assert\NotBlank
 */
public $name;

/**
 * @ORM\Column(type="string", length=255, nullable=true)
 */
public $path;

/**
 * @Assert\File(maxSize="6000000")
 */
public $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()
{

    return __DIR__.'/../../../../../web/'.$this->getUploadDir();
}

protected function getUploadDir()
{

    return 'uploads/files';
}

 /**
 * @ORM\PrePersist()
 * @ORM\PreUpdate()
 */
public function preUpload()
{
    if (null !== $this->file) {
        $this->path = sha1(uniqid(mt_rand(), true)).'.'.$this->file->guessExtension();
    }
}

/**
 * @ORM\PostPersist()
 * @ORM\PostUpdate()
 */
public function upload()
{
    if (null === $this->file) {
        return;
    }


    $this->file->move($this->getUploadRootDir(), $this->path);

    unset($this->file);
}

/**
 * @ORM\PostRemove()
 */
public function removeUpload()
{
    if ($file = $this->getAbsolutePath()) {
        unlink($file);
    }
}

我的控制器 FileController :

/**
 * @Route("/admin/media/upload")
 * @Template()
 */
public function uploadsAction (){

    $file = new File();

    $form = $this->createForm(new \Tperroin\Bundle\MediaBundle\Form\FileUploadType(), $file);

    if ($this->getRequest()->isMethod('POST')) {
        $form->bind($this->getRequest());

        if ($form->isValid()) {

            $em = $this->getDoctrine()->getManager();

            $em->persist($file);
            $em->flush();

            return $this->redirect($this->generateUrl('tperroin_home_default_index'));
        }
    }

return array('form' => $form->createView());
}

最后是我的twig模板:

<div class="widget-content">

        <form action="{{ url('tperroin_media_file_uploads') }}" method="post" {{ form_enctype(form) }} class="dropzone">

            <div class="fallback">
                <input name="file" type="file" multiple />
            </div>
        </form>

    </div>

编辑:

表单类型:

class FileUploadType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('file', 'file');
}
public function getName()
{
    return 'file';
}
}

我的代码有什么问题?我知道这与UploadedFile和数组有关,但我不知道如何解决。

非常感谢!

编辑:

也许这个链接可以帮助别人,我无法复现这个问题:

Symfony2中的多文件上传问题

使用新的uploadAction函数进行编辑:

/**
* @Route("/upload/process", name="upload_media")
*/
   public function uploadAction()
   {
   $request = $this->get('request');
   $files = $request->files;       

   $directory = $this->get('kernel')->getRootDir() . '/../web' . $this->getRequest()->getBasePath() . '/files/';

   foreach ($files as $uploadedFile) {

       $name = $uploadedFile->getClientOriginalName();
       $file = $uploadedFile->move($directory, $name);

       $upload = new File($name);

       $em = $this->get('doctrine')->getManager();

       $em->persist($upload);

       $em->flush();


   }

   return $this->redirect($this->generateUrl('tperroin_media_default_index'));
   }

你能发布你的FormType吗?但是我认为,所有的代码都期望一个文件,而不是多个文件。 - Emii Khaos
谢谢!当然,看看我的编辑。我尝试使用属性“multiple”,但是出现了相同的错误... - Perroin Thibault
你没有渲染任何字段 - 这正常吗?至少需要显示一个带有文件和csrf令牌的字段。还要在表单类型中指定data_class选项。 - Alexey B.
没错,看看我的新模板。但是我用这段代码还是有同样的错误... - Perroin Thibault
3个回答

8
首先,您实际上并没有在Twig模板中使用FileUploadType。您需要手动创建一个表单,并将action路径设置为您在控制器中定义的URL。您从表单视图传递到模板中的唯一使用是form_enctype。但像大多数多文件上传程序一样,Dropzone似乎会伪造自己的请求来上传图片。
这意味着您还需要手动处理传入的数据。
/**
 * @Route("/admin/media/upload")
 * @Template()
 */
public function showUploadAction()
{
    // just show the template
    return [];
}

/**
 * @Route("/admin/media/upload/process", name="upload_media")
 */
public function uploadAction()
{
    $request = $this->get('request');
    $files = $request->files;

    // configuration values
    $directory = //...

    // $file will be an instance of Symfony\Component\HttpFoundation\File\UploadedFile
    foreach ($files as $uploadedFile) {
        // name the resulting file
        $name = //...
        $file = $uploadedFile->move($directory, $name);

        // do something with the actual file
        $this->doSomething($file);
    }

    // return data to the frontend
    return new JsonResponse([]);
}

方法uploadAction从Symfony请求中获取FileBag,遍历它并对生成的文件执行自定义逻辑。当然,您必须自己处理File实体的存储。
您的模板应该如下所示:
<form action="{{ path('upload_media') }}" method="post" class="dropzone">
    <div class="fallback">
        <input name="file" type="file" multiple />
    </div>
</form>

感谢这个问题,我注意到了Dropzone上传器以及integrated支持它在OneupUploaderBundle中的功能。因此,您可以使用这个捆绑包,它简化了上面提到的一些步骤。

谢谢!这个解决方案似乎有效,我的文件存储在正确的目录中,但我无法持久化这些图像。请查看我的编辑和uploadAction函数。也许是UploadFile出了问题。再次感谢您的帮助。 - Perroin Thibault
你正在尝试持久化一个 Symfony\Component\HttpFoundation\File\File 的实例,但它不是 Doctrine 实体。相反,尝试将此文件添加到你的实体 File 的新实例中,并持久化该对象。 - devsheeep
1
它可以工作,谢谢!我想我理解了文件的概念。我测试了你的捆绑包,效果很棒! - Perroin Thibault

1
Symfony要求每个表单都包含一个csrf-token。您的表单不包含一个,因此Symfony尝试使用您的“file”字段作为csrf-token并抛出错误。 解决此问题的第一步是在您的表单元素中使用此代码片段呈现csrf-token:
{{ form_widget(form) }}

那么你需要更改实体,使其能够处理多个文件。这可以通过实现一对多关系(上传(一个)到文件(多个))来完成。


这仅适用于Dropzone上传器的回退部分。 - devsheeep

0

在Symfony中,如果您遇到错误

Error: Call to a member function move() on a non-object

尝试更改

$files = $request->files

$files = $request->files->get('file');

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接