punkave / symfony2-file-uploader-bundle
为Symfony2提供多文件上传功能,使用BlueImp上传工具。同时还可以缩放上传的图片。
Requires
- php: >=5.3.2
- symfony/framework-bundle: 2.*
This package is not auto-updated.
Last update: 2024-09-22 03:07:17 UTC
README
需要维护者!
此组件不再积极更新。它仅维护与Symfony 2.0.x相关的错误。如果您有兴趣接管此组件的维护,请联系 tom@punkave.com。谢谢!
简介
此组件提供基于BlueImp jQuery文件上传包的多文件上传功能。在兼容浏览器中,支持拖放和多个文件选择。我们选择BlueImp是因为它具有出色的前后兼容性。
由于BlueImp提供的现有PHP上传类已经非常优秀,并且可以直接完成许多出色的功能,因此此组件是一个相对较薄的包装。我们提供了一个方法,可以将它集成到Symfony 2项目中。
上传工具将文件发送到您指定的文件夹。如果该文件夹已包含文件,则它们将与新文件并排显示,作为可以删除的现有文件。
组件可以自动将图片缩放到您指定的尺寸。提供的方法可以实现创建表单,其中附加文件可以尊重“保存”和“取消”操作。
关于Internet Explorer的说明
10版本之前的Internet Explorer不支持多文件上传。但是IE用户将能够一次添加一个文件,并且仍然能够创建一个附加文件集合。
要求
- Symfony2
- jQuery
- jQuery UI
- Underscore
安装
Symfony 2.0
-
将以下行添加到您的Symfony2 deps文件中
[FileUploaderBundle] git=http://github.com/punkave/symfony2-file-uploader-bundle.git target=/bundles/PunkAve/FileUploaderBundle
-
使用以下行修改您的AppKernel
new PunkAve\FileUploaderBundle\PunkAveFileUploaderBundle(),
-
如果您还没有,请将以下行添加到您的autoload.php文件中
'PunkAve' => DIR.'/../vendor/bundles',
-
安装您的供应商
bin/vendors install
Symfony 2.2
-
将以下行添加到您的composer.json require块中: "punkave/symfony2-file-uploader-bundle": "dev-master"
标准的symfony 2.2 composer.json文件有一个分支别名,它会干扰安装此组件。您可以通过删除以下行来解决这个问题:
"branch-alias": {
"dev-master": "2.2-dev"
}
-
使用以下行修改您的AppKernel
new PunkAve\FileUploaderBundle\PunkAveFileUploaderBundle(),
-
执行composer install
使用方法
您的页面必须包含Underscore模板以渲染文件列表和上传器。您可以使用我们的模板如下所示
{# Underscore templates for the uploader #}
{% include "PunkAveFileUploaderBundle:Default:templates.html.twig" %}
这样做就足够了,您可以在身体中的任何地方这样做。如果您愿意,可以复制并修改templates.html.twig,并将其包含在您自己的目录中。只需不要删除data-*属性。其余的标记取决于您。
在编辑操作中
假设您在控制器中有一个editAction()方法。您有一个表单,您希望在其中包含一个附加文件列表,使其像表单中的其他字段一样工作:您可以添加更多,您可以删除现有文件,但除非用户点击“保存”,否则不会发生任何永久操作。
FileUploader服务需要为特定对象附加的文件提供一个唯一的文件夹名称。为了为新对象和现有对象完成这一任务,我们建议您遵循“editId模式”,在该模式中,一个表单在其整个生命周期内(包括必要的多次验证)被分配一个唯一、随机的“editId”。这使得我们能够管理尚未拥有自己的ID的新对象的文件上传。
此代码在表单的第一次遍历时创建editId,并在必要时同步现有对象附加的现有文件。from_folder和to_folder对象指定了存储附加文件的子目录。稍后我们将探讨这些文件夹的父目录是如何确定的。
(获取$posting并验证用户是否有权编辑该帖子由您负责。)
$request = $this->getRequest();
$editId = $this->getRequest()->get('editId');
if (!preg_match('/^\d+$/', $editId))
{
$editId = sprintf('%09d', mt_rand(0, 1999999999));
if ($posting->getId())
{
$this->get('punk_ave.file_uploader')->syncFiles(
array('from_folder' => 'attachments/' . $posting->getId(),
'to_folder' => 'tmp/attachments/' . $editId,
'create_to_folder' => true));
}
}
如果用户在尝试完成操作时遇到验证错误(例如,表单验证错误),您将希望再次显示相同的文件列表。因此,请使用getFiles方法获取现有文件列表。请确保将此列表传递给您的模板。
$existingFiles = $this->get('punk_ave.file_uploader')->getFiles(array('folder' => 'tmp/attachments/' . $editId));
(请注意,您生成的editId应该是高度随机的,以防止用户控制彼此的附件。)
当用户保存表单并且您刚刚持久化帖子对象时,您还应该将临时文件夹中与editId关联的文件同步到与帖子ID关联的永久文件夹。由于我们已经完成了临时文件夹,我们要求文件上传服务删除该文件夹。我们还要求服务在必要时创建目标文件夹
$fileUploader = $this->get('punk_ave.file_uploader');
$fileUploader->syncFiles(
array('from_folder' => '/tmp/attachments/' . $editId,
'to_folder' => '/attachments/' . $posting->getId(),
'remove_from_folder' => true,
'create_to_folder' => true));
稍后您可以轻松地获取附加到对象的所有文件名称列表
$files = $fileUploader->getFiles(array('folder' => 'attachments/' . $posting->getId()));
然而,访问文件系统会有性能成本。您会发现将附件列表保存在Doctrine表中更有效率,尤其是如果您想在列表视图中包括第一个附件。只需使用getFiles获取文件名列表,然后按照您的需求在数据库中镜像该列表。
在您的布局中
为了通过Assetic提供必要的JavaScript(请注意,您必须提供jQuery、jQuery UI和Underscore)
{% javascripts
'@MyBundle/Resources/public/js/jquery-1.7.2.min.js'
'@MyBundle/Resources/public/js/jquery-ui-1.8.22.custom.min.js'
'@MyBundle/Resources/public/js/underscore-min.js'
'@PunkAveFileUploaderBundle/Resources/public/js/jquery.fileupload.js'
'@PunkAveFileUploaderBundle/Resources/public/js/jquery.iframe-transport.js'
'@PunkAveFileUploaderBundle/Resources/public/js/FileUploader.js' %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}
您必须包括iframe传输,以确保与IE 9及以下版本兼容。
在编辑模板中
假设有一个与编辑操作关联的edit.html.twig模板。这可能看起来像这样。请注意,您的操作中的渲染调用将传递帖子对象、editId、现有文件数组以及isNew标志
{% extends "MyBundle:Default:layout.html.twig" %}
{% block body %}
{# Underscore templates for the uploader #}
{% include "PunkAveFileUploaderBundle:Default:templates.html.twig" %}
<form class="edit-form" action="{{ path('edit', { id: posting.id, editId: editId }) }}" method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
{# Hydrated by javascript #}
<div class="file-uploader"></div>
<button class="btn btn-primary" type="submit">{{ isNew ? "Save New Listing" : "Save Changes" }}</button>
<a class="btn" href="{{ cancel }}">Cancel</a>
{% if not isNew %}
<a class="btn btn-danger" href="{{ path('delete', { id: posting.id } ) }}">Delete</a>
{% endif %}
</form>
<script type="text/javascript">
// Enable the file uploader
$(function() {
new PunkAveFileUploader({
'uploadUrl': {{ path('upload', { editId: editId }) | json_encode | raw }},
'viewUrl': {{ ('/uploads/tmp/attachments/' ~ editId) | json_encode | raw }},
'el': '.file-uploader',
'existingFiles': {{ punkave_get_files('tmp/attachments/' ~ editId) | json_encode | raw }},
'delaySubmitWhileUploading': '.edit-form'
});
});
</script>
{% endblock %}
进度显示
template.html.twig中有一个简单的旋转器。如果您选择提供自己的Underscore模板,可以将其替换。只需确保您有自己的元素,并带有data-spinner="1"属性。
如果您正在使用template.html.twig,请注意,您必须以通常的方式发布您的资产,以便旋转器图像可用
php app/console assets:install web/
作为替代,您可以在间隔计时器上编写自己的代码,检查PunkAveFileUploader对象的uploading属性是否当前设置为true,并基于此显示旋转器。
延迟表单提交直到上传完成
如果上传仍在进行中,让用户提交文件上传器所属的表单并不是一个好主意。您可以通过在上创建PunkAveFileUploader JavaScript对象时指定'delaySubmitWhileUploading'选项来轻松地阻止此操作
'delaySubmitWhileUploading': '.edit-form'
或者,您可以在任何时间检查使用new PunkAveFileUploader(...)创建的对象的uploading属性。如果正在上传,它将为true。现有实现中的delaySubmitWhileUploading依赖于这一点。
在上传操作中
除了您表单的常规编辑操作之外,还必须有一个上传操作来处理文件上传。这个操作将调用服务的handleFileUpload方法,将任务传递给BlueImp的UploadHandler类。由于该类直接在PHP中实现整个REST响应,所以该方法不会返回。
以下是上传操作
/**
*
* @Route("/upload", name="upload")
* @Template()
*/
public function uploadAction()
{
$editId = $this->getRequest()->get('editId');
if (!preg_match('/^\d+$/', $editId))
{
throw new Exception("Bad edit id");
}
$this->get('punk_ave.file_uploader')->handleFileUpload(array('folder' => 'tmp/attachments/' . $editId));
}
这个单一操作实际上实现了一个完整的REST API,其中BlueImp UploadHandler类负责上传和删除文件。
再次强调,handleFileUpload不会返回,因为响应是由BlueImp的UploadHandler类以原生PHP生成的。
设置允许的文件类型
您可以通过在handleFileUpload方法或parameters.yml中指定它们,来指定自定义文件类型,从而偏离默认类型(这些类型在Resources/config/services.yml中定义)。
在handleFileUpload中
$this->get('punk_ave.file_uploader')->handleFileUpload(array(
'folder' => 'tmp/attachments/' . $editId,
'allowed_extensions' => array('zip', 'rar', 'tar')
));
在这种情况下,FileUploader服务将默认扩展名与提供的扩展名合并,并创建一个单独的正则表达式。使用正则表达式字符可能会导致错误。
Parameters.yml: 如果您已安装Symfony标准版,您可以在app/config/parameters.yml中指定它们。
file_uploader.allowed_extensions:
- zip
- rar
- tar
这样做将覆盖默认扩展名而不是添加它们!
删除文件
迟早帖子会被删除,您希望所有附件也一并删除。
您可以这样做
$this->get('punk_ave.file_uploader')->removeFiles(array('folder' => 'attachments/' . $posting->getId()));
您可能想在管理类或Doctrine事件监听器中这样做,但这不是我们的职责。
删除临时文件
如果您选择遵循我们的editId模式,您将希望定期清除web/uploads/tmp中超过一定年龄的内容。人们经常离开网站,所以并不是每个人都会点击您精心提供的“取消”操作,该操作根据editId模式调用removeFiles()。
考虑安装此shell脚本作为夜间运行的cron作业。此shell脚本删除超过一天的文件,然后删除空文件夹
#!/bin/sh
find /path/to/my/project/web/uploads/tmp -mtime +1 -type f -delete
find /path/to/my/project/web/uploads/tmp -mindepth 1 -type d -empty -delete
(由于第二个命令不是递归的,父文件夹可能多停留一天,但它们将在第二天被删除。)
配置参数
请参阅此包中的Resources/config/services.yml
。您可以轻松决定上传的父文件夹是什么,以及接受的文件扩展名,以及您希望将图像文件自动缩放到的大小。
上述from_folder
、to_folder
和folder
选项在处理文件时都附加到file_uploader.file_base_path
之后。
如果file_uploader.file_base_path
设置为以下内容(这是默认设置)
file_uploader.file_base_path: "%kernel.root_dir%/../web/uploads"
并且当调用handleFileUpload
时,folder
选项设置为attachments/5
,那么上传的文件将到达
/root/of/your/project/web/uploads/attachments/5/originals
如果这个帖子的唯一附件是botfly.jpg
,并且您已为file_uploader.sizes
选项(默认情况下我们提供几个有用的标准尺寸)配置了一个或多个图像尺寸,那么您将看到
/root/of/your/project/web/uploads/photos/5/originals/botfly.jpg
/root/of/your/project/web/uploads/photos/5/thumbnail/botfly.jpg
/root/of/your/project/web/uploads/photos/5/medium/botfly.jpg
/root/of/your/project/web/uploads/photos/5/large/botfly.jpg
因此,所有这些都可以通过以下URL轻松访问
/uploads/photos/5/originals/botfly.jpg
等等。
尽可能保留上传文件的原始名称和文件扩展名,同时不引入安全风险。
限制上传数量
您可以通过设置max_no_of_files
属性来限制上传文件的数量。您可以在parameters.yml中设置如下
parameters:
file_uploader.max_number_of_files: 4
您可能想为这种情况添加一个错误处理器。在初始化PunkAveFileUploader的模板中设置errorCallback
// Enable the file uploader
$(function() {
new PunkAveFileUploader({
// ... other required options,
'errorCallback': function(errorObj) {
if (errorObj.error == 'maxNumberOfFiles') {
alert("Maximum uploaded files exceeded!");
}
}
});
});
限制
此包通过glob()
函数访问文件系统。它不会与S3流包装器无缝配合。
在大型附件的情况下,根据editId模式进行文件来回同步可能不太令人满意。在这种情况下,不要使用editId模式。一个替代方案是在数据库中立即创建对象,直到你标记它们为活动状态才在列表视图中显示它们。这样,你的编辑操作可以使用对象的永久ID作为folder
选项的一部分,而无需进行同步。在这种情况下,你可能需要将附件列表移至表单下方,提示用户没有“取消”这些操作的东西。
备注
上传器已使用Bootstrap约定进行样式设计。如果你的项目中包含Bootstrap,上传器应该能够直接看起来相当美观。
“选择文件”按钮允许多选以及拖放。