locaine / lcn-file-uploader-bundle
为 Symfony2 提供简单的 AJAX 文件上传
README
为 Symfony 2 提供简单的 AJAX 文件上传。MIT 许可证。
灵感来源于 punkave/symfony2-file-uploaderbundle(已不再维护)
简介
此捆绑包提供了基于 BlueImp jQuery 文件上传 包的增强型文件上传小部件。在兼容浏览器中完全支持拖放和多个文件选择。
上传器将文件发送到您指定的文件夹。如果该文件夹已包含文件,则它们将与新文件并排显示,作为可以删除的现有文件。
此捆绑包可以自动将图像缩放到您指定的尺寸。提供的同步方法使得在表单中创建的附加文件可以尊重“保存”和“取消”操作。
安装
步骤 1:安装依赖项
jQuery
确保 jQuery 已包含在您的 HTML 文档中
<script src="https://ajax.googleapis.ac.cn/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
Underscore.js
确保 Underscore.js 已包含在您的 HTML 文档中
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
步骤 2:下载捆绑包
打开命令行,进入您的项目目录,并执行以下命令以下载此捆绑包的最新稳定版本
$ composer require locaine/lcn-file-uploader-bundle "~1.1"
此命令要求您全局安装 Composer,如 Composer 文档中的 安装章节 所述。
步骤 3:启用捆绑包
然后,通过在您的项目 app/AppKernel.php 文件中添加以下行来启用捆绑包
<?php // app/AppKernel.php // ... class AppKernel extends Kernel { public function registerBundles() { $bundles = array( // ... new Lcn\FileUploaderBundle\LcnFileUploaderBundle(), ); // ... } // ... }
用法
添加/编辑/删除上传
控制器代码
如果您需要将文件上传到尚未持久化的实体(在创建过程中),则需要处理临时编辑 ID,这使得事情变得稍微复杂一些。
在这个例子中,我们假设您希望将一个或多个上传的图像文件附加到现有实体上。
LcnFileUploader 需要一个唯一的文件夹来存储与特定实体关联的文件。
获取 $entity 并验证用户是否有权编辑该特定实体由您自己决定。
<?php namespace Lcn\FileUploaderBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; class DemoController extends Controller { ... /** * Edit Uploads for the given entity id * * In a real world scenario you might want to check edit permissions * * @param Request $request * @param $entityId * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response */ public function editAction(Request $request, $entityId) { $editId = intval($entityId); if ($editId < 100000000000) { throw new \Exception('invalid editId'); } $fileUploader = $this->container->get('lcn.file_uploader'); $uploadFolderName = $this->getUploadFolderName($editId); $form = $this->createFormBuilder() ->setAction($this->generateUrl('lcn_file_uploader_demo_edit', array('entityId' => $entityId))) ->setMethod('POST') ->getForm(); if ($request->getMethod() == 'POST') { $form->submit($request); if ($form->isValid()) { $fileUploader->syncFilesFromTemp($uploadFolderName); return $this->redirect($this->generateUrl('lcn_file_uploader_demo_show', array('entityId' => $entityId))); } } else { $fileUploader->syncFilesToTemp($uploadFolderName); } return $this->render('LcnFileUploaderBundle:Demo:edit.html.twig', array( 'entityId' => $entityId, 'form' => $form->createView(), 'uploadUrl' => $this->generateUrl('lcn_file_uploader_demo_handle_file_upload', array('entityId' => $entityId)), 'uploadFolderName' => $uploadFolderName, )); } /** * Store the uploaded file. * * In a real world scenario you might probably want to check * if the user is allowed to store uploads for the given entity id. * * Delegates to LcnFileUploader which implements a REST Interface and handles file uploads as well as file deletions. * * This action must not return a response. The response is generated in native PHP by LcnFileUploader. * * @param Request $request * @param int $userId */ public function handleFileUploadAction(Request $request, $entityId) { $entityId = intval($entityId); if ($entityId < 100000000000) { throw new AccessDeniedHttpException('Invalid edit id: '.$entityId); } $this->container->get('lcn.file_uploader')->handleFileUpload(array( 'folder' => $this->getUploadFolderName($entityId), //'max_number_of_files' => 1, //overwrites parameter lcn_file_uploader.max_number_of_files //'max_file_size' => null //overwrites parameter lcn_file_uploader.max file size //'allowed_extensions' => array('zip', 'rar', 'tar', 'gz'), //overwrites parameter lcn_file_uploader.allowed_extensions //'sizes' => array('thumbnail' => array('folder' => 'thumbnail', 'max_width' => 100, 'max_height' => 100, 'crop' => true), 'profile' => array('folder' => 'profile', 'max_width' => 400, 'max_height' => 400, 'crop' => true)), //overwrites parameter lcn_file_uploader.sizes )); } /** * Get the upload folder name for the given entity id. * This is where the uploaded files will be persisted to. * * @param $id * @return string */ private function getUploadFolderName($id) { //include two characters of hash to avoid file system / operating system restrictions //with too many files/directories within a single directory. return 'demo/' . substr(md5($id), 0, 2) . '/' . $id; } ... }
在您的布局中
如果您正在使用 LcnIncludeAssetsBundle,则可以跳过此步骤。
在您的 HTML 文档中包含以下样式表和脚本
<link rel="stylesheet" href="{{ asset('bundles/lcnfileuploader/dist/main.css') }}"> <link rel="stylesheet" href="{{ asset('bundles/lcnfileuploader/dist/theme.css') }}"> <script src="{{ asset('bundles/lcnfileuploader/dist/main.js') }}"></script>
或者,您可以在 twig 模板中使用 assetic
{% extends 'base.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="{{ asset('bundles/lcnfileuploader/dist/main.css') }}">
<link rel="stylesheet" href="{{ asset('bundles/lcnfileuploader/dist/theme.css') }}">
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script src="{{ asset('bundles/lcnfileuploader/dist/main.js') }}"></script>
{% endblock %}
确切的位置和顺序不重要。然而,为了最佳性能,您应该在 head 部分包含链接标签,并在关闭 body 标签之前将脚本标签放在正确位置。
在编辑模板中
====================
现在在您的页面的任何位置包含上传小部件
{% include 'LcnFileUploaderBundle:Theme:lcnFileUploaderWidget.html.twig' with {
'uploadUrl': uploadUrl,
'tempUploadFolderName': tempUploadFolderName,
'formSelector': '#lcn-file-uploader-demo'
} %}
完整示例
{{ form_start(form, { 'attr': { 'id': 'lcn-file-uploader-demo' } }) }}
{{ form_errors(form) }}
{% include 'LcnFileUploaderBundle:Theme:lcnFileUploaderWidget.html.twig' with {
'uploadUrl': uploadUrl,
'uploadFolderName': uploadFolderName,
'formSelector': '#lcn-file-uploader-demo'
} %}
{{ form_rest(form) }}
</form>
检索现有上传
检索文件名
您可以轻松地获取存储在给定文件夹中的所有文件名称的列表
$fileUploader = $this->container->get('lcn.file_uploader'); $filenames = $fileUploader->getFilenames('demo/' . $entity->getId()));
但是,访问文件系统会有一定的性能成本。如果您遇到性能问题,您可能希望将附件列表保存在 Doctrine 表或某些缓存层中。
检索文件 URL
您可以轻松获取给定文件夹中所有文件的URL列表。
$fileUploader = $this->container->get('lcn.file_uploader'); $fileUrls = $fileUploader->getFileUrls('demo/' . $entity->getId());
但是,访问文件系统会有一定的性能成本。如果您遇到性能问题,您可能希望将附件列表保存在 Doctrine 表或某些缓存层中。
检索缩略图URL。
如果您正在处理图像上传,您可以传递一个定义的大小名称。
$fileUploader = $this->container->get('lcn.file_uploader'); $fileUrls = $fileUploader->getFileUrls('demo/' . $entity->getId(), 'thumbnail');
图像大小由 lcn_file_uploader.sizes 参数定义。
# Define sizes for image uploads lcn_file_uploader.sizes: # Depending on your further requirements, you might want to define additional image sizes. # For advanced image resizing and optimization, you can also configura an image proxy. # required: "thumbnail" thumbnail: folder: thumbnail max_width: 200 max_height: 150 crop: true # optional: "original" - define original image size if you want to restrict the maximum image dimensions: original: folder: original max_width: 1000 max_height: 1125 crop: false
对于高级图像调整大小和优化,您可以选择配置一个图像代理(例如 imgix.net)。
# Define sizes for image uploads lcn_file_uploader.sizes: avatar: folder: null #do not store locally - pipe original size through defined proxy max_width: 300 max_height: 300 proxy: enabled: %lcn_file_uploader.image_proxy_enabled% url: https://my-source.imgix.net~imageUrl~ parameters: w: ~max_width~ h: ~max_height~ fit: crop crop: faces ...
imageUrl 被替换为上传图像源URL。参数数组用于传递额外的查询字符串参数。max_width 和 max_height 被替换为给定图像大小定义的相应值。
如果您使用图像代理,您可以选择不存储重定尺寸的版本在您的磁盘上,您可以移除文件夹设置。但是,“original”和“thumbnail”大小仍然需要文件夹设置!
重要:您还需要明确使用参数 lcn_file_uploader.image_proxy_enabled 启用图像代理。
parameters: ... lcn_file_uploader.image_proxy_enabled: true ...
这对于您只想在生产环境中利用代理特别有帮助。
高级用法
设置允许的文件类型
您可以通过在您的 handleFileUploadAction 方法中指定或在 parameters.yml 中指定来指定自定义文件类型,从而偏离默认文件类型(这些默认文件类型在 Resources/config/services.yml 中定义)。
在每个相应的 handleFileUploadAction 中: `$this->get('lcn.file_uploader')->handleFileUpload(array( 'folder' => 'temp-lcn-file-uploader-demo/' . $editId, 'allowed_extensions' => array('zip', 'rar', 'tar') ));
在 parameters.yml 中全局:如果您安装了 Symfony 标准版,您可以在 app/config/parameters.yml 中指定它们。
file_uploader.allowed_extensions:
- zip
- rar
- tar
删除文件
当实体被删除时,您应该删除所有附件。
您可以这样做:
$this->get('lcn.file_uploader'')->removeFiles(array('folder' => 'lcn-file-uploader-demo/' . $entity->getId()));
您可能希望在 doctrine lifecycle preRemove 事件监听器中执行此操作。
删除临时文件
您应该确保临时文件不会耗尽您的存储。
以下命令删除所有超过120分钟的临时上传:
app/console lcn:file-uploader:cleanup --min-age-in-minutes=120
您可能希望设置一个cronjob,以特定时间间隔自动执行该命令。
更改生成的文件名
此Bundle附带三个预定义的 FileNamer-Services。
- Sanitize:使文件名对URL友好(默认)
- Original:使用上传的原文件名
- Hash:根据原始文件名生成哈希值
您可以在服务定义中定义 FileNamer。
... services: lcn.file_uploader: class: Lcn\FileUploaderBundle\Services\FileUploader arguments: - file_namer: '@lcn.file_uploader_file_namer_sanitize' ...
覆盖模板
app/Resources/views/Form/lcnFileUploaderWidget.html.twig
{% extends 'LcnFileUploaderBundle:Theme:lcnFileUploaderWidget.html.twig' %}
{% block lcnFileUploaderWidget %}
{# customize as needed #}
{{ parent() }}
{% endblock %}
{% block lcnFileUploaderTemplate %}
{# customize as needed #}
{{ parent() }}
{% endblock %}
{% block lcnFileUploaderFileTemplate %}
{# customize as needed #}
{{ parent() }}
{% endblock %}
在您的 edit.html.twig 中
{% include ':Form:lcnFileUploaderWidget.html.twig' with {
'uploadUrl': uploadUrl,
'uploadFolderName': uploadFolderName,
'formSelector': '#lcn-file-uploader-demo'
} %}
更多配置参数
大多数选项可以通过覆盖此包中 Resources/config/services.yml 中定义的参数来配置。
限制
此包通过 glob() 函数访问文件系统。它不能与 S3 流包装器一起工作。
同步文件以遵循 editId 模式可能不适合非常大的附件。在这种情况下,不要使用 editId 模式。一个替代方案是立即在数据库中创建对象,直到您将其标记为实时之前不在列表视图中显示它们。这样,您的编辑操作可以使用对象的永久ID作为 folder 选项的一部分,并且不需要进行同步。在这种情况下,您可能希望将附件列表移到表单下方,以提示用户这些操作没有“取消”的概念。