介绍
我正在Windows 10 Pro
使用XAMPP
[1] 进行开发,其中包括PHP v7.0.9
.
我正在使用 Symfony v3.1.5、OneupUploaderBundle和Blueimp jQuery 上传来将文件上传到服务器。
在进行设置时,我遵循了OneUpUploaderBundle
[2] 和jQuery file upload
[3]、[4] 的文档。
问题
我想将文件上传到某个目录,然后检查它们的mime 类型并验证是否允许上传的文件 mime 类型,然后 - 将它们移动到自定义目录(可以从文件更改为文件),最后我想保留文件路径和文件名到数据库。
文件上传工作正常,文件上传到oneup_uploader_endpoint('gallery')
. 甚至自定义文件Namer
也可以工作并允许上传到自定义目录。
监听器被调用,但它们显示在 Symfony Profiler Events 部分Not Called Listeners
。我怀疑这可能是因为Blueimp jQuery uploader
为了上传文件和显示上传进度而执行的 XMLHttpRequest(Ajax 请求)。
目前我使用 Javascript 来过滤允许的 mime 类型。它有帮助,但它不是“值得信赖的”。例如,如果有名称为test.pdf.jpg
javascript 的 PDF 文件将根据白名单中的扩展名允许它。但是,如果 PDF 文件有名称怎么办test.jpg
——没有人能确定真正的 mimetype。因此需要在服务器上进行验证。我也在服务器端使用 mimetype 过滤器。
问题是 - 如果文件 mimetype 不在列表中 - 文件,简单地说,没有移动到目标目录中 - 从技术上讲,它上传很好,因为服务器上的验证发生在完全上传之后。(如果我理解正确的话。如果我对此有误,请纠正我。)而且我不知道如何向用户获取/显示任何关于上传被 mimetype 过滤器(白名单)过滤的消息。
问题
所以问题是:Blueimp jQuery 上传器成功上传文件后,如何向用户显示关于未通过白名单的文件的反馈。
代码
我的服务.yml
services:
app.ultra_helpers:
class: AppBundle\UltraHelpers\UltraHelpers
arguments: ['@service_container']
app.upload_listener:
class: AppBundle\EventListener\UploadListener
arguments: ["@doctrine.orm.entity_manager", "@session", "@service_container"]
tags:
- { name: kernel.event_listener, event: oneup_uploader.pre_upload.gallery, method: onUpload }
- { name: kernel.event_listener, event: oneup_uploader.post_upload.gallery, method: onPostUpload }
app.alowed_mimetype_listener:
class: AppBundle\EventListener\AllowedMimetypeValidationListener
tags:
- { name: kernel.event_listener, event: oneup_uploader.validation.gallery, method: onValidate }
app.upload_unique_namer:
class: AppBundle\Uploader\Naming\UploadUniqueNamer
arguments: ["@session"]
我的自定义命名器
<?php
namespace AppBundle\Uploader\Naming;
use Oneup\UploaderBundle\Uploader\File\FileInterface;
use Oneup\UploaderBundle\Uploader\Naming\NamerInterface;
use Symfony\Component\HttpFoundation\Session\Session;
class UploadUniqueNamer implements NamerInterface
{
private $session;
public function __construct(Session $session)
{
$this->session = $session;
}
/**
* Creates a user directory name for the file being uploaded.
*
* @param FileInterface $file
* @return string The directory name.
*/
public function name(FileInterface $file)
{
$active_project_name = $this->session->get('active_project_name');
$active_project_path = $this->session->get('active_project_path');
$upload_files_path = $active_project_name . $active_project_path;
$unique_name = uniqid();
return sprintf('%s/%s_%s',
$upload_files_path,
$unique_name,
$file->getClientOriginalName()
);
}
}
我的 config.yml
oneup_uploader:
mappings:
gallery:
storage:
type: flysystem
filesystem: oneup_flysystem.gallery_filesystem
frontend: blueimp
enable_progress: true
namer: app.upload_unique_namer
allowed_mimetypes: [ image/png, image/jpg, image/jpeg, image/gif ]
max_size: 10485760s
oneup_flysystem:
adapters:
my_adapter:
local:
directory: "%kernel.root_dir%/../data"
filesystems:
gallery:
adapter: my_adapter
我的上传监听器:
<?php
namespace AppBundle\EventListener;
use Oneup\UploaderBundle\Event\PreUploadEvent;
use Oneup\UploaderBundle\Event\PostUploadEvent;
use Doctrine\ORM\EntityManager;
use Symfony\Component\DependencyInjection\Container as Container;
use Symfony\Component\HttpFoundation\Session\Session;
use AppBundle\Entity\MyFile;
class UploadListener
{
/**
* @var EntityManager
*/
private $entityManager;
/**
* @var Session
*/
private $session;
/**
* @var Container
*/
private $container;
/**
* @var originalName
*/
protected $originalName;
public function __construct(EntityManager $entityManager, Session $session, Container $container)
{
$this->entityManager = $entityManager;
$this->session = $session;
$this->container = $container;
}
public function onUpload(PreUploadEvent $event)
{
$file = $event->getFile();
$this->originalName = $file->getClientOriginalName();
}
public function onPostUpload(PostUploadEvent $event)
{
$ultra = $this->container->get('app.ultra_helpers');
$file_path = $ultra->filterFileNameFromPath($event->getFile());
$upload_files_path = $this->session->get('active_project_path');
$my_file = new MyFile();
$my_file->setName($upload_files_path);
$my_file->setFile($file_path);
$this->entityManager->persist($my_file);
$this->entityManager->flush();
}
}
我的模板显示上传表单upload.html.twig
{% extends 'base.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" type="text/css" href="{{ asset('css/blueimp/jquery.fileupload.css') }}" />
<link rel="stylesheet" type="text/css" href="{{ asset('css/bootstrap/bootstrap.css') }}" />
<link rel="stylesheet" type="text/css" href="{{ asset('css/bootstrap/bootstrap-theme.css') }}" />
{% endblock %}
{% block content %}
<div id="box-list" class="clearfix">
Go to: <a href="{{ path('file_list') }}">File list</a>
</div>
<div id="box-upload">
<div id="box-file-upload">
{{ form_start(form, {'attr': {'id': 'my-form-upload'}, 'method': 'POST'}) }}
<div class="row form-message">
{{ form_errors(form) }}
</div>
<span class="btn btn-success fileinput-button">
<i class="glyphicon glyphicon-plus"></i>
<span> Choose files...</span>
{{ form_row(form.file, {'id': 'file-upload', 'attr': {'name': 'files[]', 'data-url': oneup_uploader_endpoint("gallery") }}) }}
</span>
{{ form_end(form) }}
</div>
<div id="box-progress">
<div id="box-progress-bar" style="width: 0%;"></div>
</div>
<div id="box-info">
<p>Upload status...</p>
</div>
</div>
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script type="text/javascript" src="{{ asset('js/blueimp/jquery.ui.widget.js') }}"></script>
<script type="text/javascript" src="{{ asset('js/blueimp/jquery.iframe-transport.js') }}"></script>
<script type="text/javascript" src="{{ asset('js/blueimp/jquery.fileupload.js') }}"></script>
<script type="text/javascript">
$(function()
{
'use strict';
$('#file-upload').on('click', function ()
{
$('#box-progress-bar').css('width', '1%');
});
$('#file-upload').on("fileuploadprocessfail", function(e, data)
{
var file = data.files[data.index];
alert(file.error);
console.log(file.error);
});
$('#file-upload').fileupload({
dataType: 'json',
add: function (e, data)
{
var fileName = data.files[0].name;
var fileType = data.files[0].name.split('.').pop();
var allowedTypes = 'jpg,JPG,jpeg,JPEG,png,PNG,gif,GIF,pdf,PDF';
if (allowedTypes.indexOf(fileType) < 0)
{
$('#box-progress-bar').css('width', '0');
$('<p/>').text(fileName).appendTo($('#box-info'));
$('<p class="wrong-file-type"/>').text('Invalid file type').appendTo($('#box-info'));
return false;
}
else
{
$('<p/>').text(fileName).appendTo($('#box-info'));
if ($('.button-upload').length == 0)
{
// disabling file input
$('input#file-upload').attr('disabled', true);
data.context = $('<button class="button-upload btn btn-primary start"/>').text('Upload')
.appendTo($('#box-info'))
.click(function ()
{
data.context = $('<p class="upload-success"/>').text('Uploading...').replaceAll($(this));
($('.button-cancel')).remove();
data.submit();
});
$('<button class="button-cancel btn btn-warning cancel" />').text('Cancel')
.appendTo($('#box-info'))
.click(function ()
{
$('#box-progress-bar').css('width', '0');
//console.log('testing');
var message = 'Upload canceled';
($('.button-upload')).remove();
($('.button-cancel')).remove();
$('<p class="wrong-file-type"/>').text(message).appendTo($('#box-info'));
// enabling file input
$('input#file-upload').attr('disabled', false);
});
}
}
},
progressall: function (e, data)
{
var progress = parseInt(data.loaded / data.total * 100, 10);
$('#box-progress-bar').css('width', progress + '%');
},
done: function (e, data)
{
data.context.text('Upload finished.');
// enabling file input
$('input#file-upload').attr('disabled', false);
}
});
});
</script>
{% endblock %}
允许的 mimetype 侦听器
<?php
namespace AppBundle\EventListener;
use Oneup\UploaderBundle\Event\ValidationEvent;
use Oneup\UploaderBundle\Uploader\Exception\ValidationException;
class AllowedMimetypeValidationListener
{
public function onValidate(ValidationEvent $event)
{
$config = $event->getConfig();
$file = $event->getFile();
if (count($config['allowed_mimetypes']) == 0)
{
return;
}
$mimetype = $file->getMimeType();
if (!in_array($mimetype, $config['allowed_mimetypes']))
{
throw new ValidationException('error.whitelist');
}
}
}
更新
- 向问题添加了允许的 mimetype 侦听器代码
结论
请指教。
感谢您的时间和知识。