1

介绍

我正在Windows 10 Pro使用XAMPP[1] 进行开发,其中包括PHP v7.0.9.

我正在使用 Symfony v3.1.5、OneupUploaderBundleBlueimp 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.jpgjavascript 的 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>&nbsp;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');
        }
    }
}

更新

  1. 向问题添加了允许的 mimetype 侦听器代码

结论

请指教。

感谢您的时间和知识。

4

0 回答 0