Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

SonataAdminBundle AJAX загрузка файлов

Anna | 31.05.2014 | нет комментариев

Всем славного времени суток. В данной статье, я хочу разглядеть 2 метода не вовсе обыкновенной загрузки файлов, которые мне по долгу службы пришлось реализовать на одном плане. Задача стояла такая: нужно реализовать Drag & Drop закачку файлов в админ части сайта, тот, что был сделан на framefork’e Symfony 2.3.* SonataAdminBundle. По ряду причин я спускаю ту часть, в которой Соната ставилась (если появится надобность то дозволено и восполнить данный пробел). Выходит, я предполагаю что у вас теснее установлена Соната и сделана правда бы одна сущность в папке Entity. Если же нет, давайте сделаем это. Добродушно пожаловать под кат:

// MyFolder/MyBundle/Entity/Name

<?php
namespace MyFolderMyBundleEntity;

use DoctrineORMMapping as ORM;
use SymfonyComponentValidatorConstraints as Assert;

/**
 * Table
 *
 * @ORMTable(name="table")
 * @ORMEntity
 */
class Table
{
    /**
     * @var integer
     *
     * @ORMColumn(name="id", type="integer")
     * @ORMId
     */
    private $id;

    /**
     * @var string
     *
     * @ORMColumn(name="name", type="string", length=255, nullable=true)
     */
    private $filePath;
}

Дальше сгенерим геттеры и сеттеры. Заходим в терминале/консоли:

$ app/console doctrine:generate:entities MyFolder/MyBundle/Entity/Name

Позже того, как сгенерировались геттеры и сеттеры, мы приступаем к Сонате. Выходит, код, нашего сонатовского файла будет таким:

// MyFolder/MyBundle/Admin/Name

<?php
namespace MyFolderMyBundleAdmin;

use SonataAdminBundleAdminAdmin;
use SonataAdminBundleDatagridListMapper;
use SonataAdminBundleDatagridDatagridMapper;
use SonataAdminBundleFormFormMapper;
use SonataAdminBundleShowShowMapper;

class NameAdmin extends Admin
{
    protected function configureFormFields(FormMapper $formMapper)
    {
        $formMapper
            ->add('name');
    }

    protected function configureDatagridFilters(DatagridMapper $datagridMapper)
    {
        $datagridMapper
            ->add('name')
    }

    protected function configureListFields(ListMapper $listMapper)
    {
        $listMapper
            ->addIdentifier('name')
    }
}

Огромнее нам здесь ничего не нужно делать. На минуту остановимся, и пойдем по этой ссылке —github.com/weixiyen/jquery-filedrop. Здесь нас волнует библиотека, там только один js файл, так что не промахнетесь :) .

Выходит. Начинается самое увлекательное, потому как нам нужно реализовать Drag & Drop, давайте его и реализуем. Для этого мы сделаем следующее, в папке MyBundle/Resources/view/Admin (Если такой нет, сделайте, путаницы потом поменьше будет) создаем файлик образца twig — sonata_admin_base_layout.html.twig с таким содержимым:

// MyBundle/Resources/view/Admin/sonata_admin_base_layout.html.twig

{% extends 'SonataAdminBundle::standard_layout.html.twig' %}

{% block stylesheets %}
    {{ parent() }}
    <link href="{{ asset('bundles/mybundle/css/admin/style.css') }}" rel="stylesheet" type="text/css" />
{% endblock %}

{% block javascripts %}
    {{ parent() }}
    <script src="{{ asset('bundles/mybundle/js/admin/jquery.filedrop.js') }}"></script>
    <script src="{{ asset('bundles/mybundle/js/admin/js.fileDropBlock.js') }}"></script>
    <script src="{{ asset('bundles/mybundle/js/admin/js.fileLoadByDefault.js') }}"></script>
    <script src="{{ asset('bundles/mybundle/js/admin/init.js') }}"></script>
{% endblock %}

Позже идем в config.yml и переопределим стержневой образец сонаты
// app/config/config.yml

sonata_admin:
    title: My Admin Panel
    templates:
        ## default global templates
        layout:  MyFolderMyBundle:Admin:sonata_admin_base_layout.html.twig

Выходит, что мы сделали, мы переопределили основной образец сонаты, Дабы иметь вероятность внедрять свои файлы в него. Вы безусловно могли заменить вот эти 4 строчки:

<script src="{{ asset('bundles/mybundle/js/admin/jquery.filedrop.js') }}"></script>
<script src="{{ asset('bundles/mybundle/js/admin/js.fileDropBlock.js') }}"></script>
<script src="{{ asset('bundles/mybundle/js/admin/js.fileLoadByDefault.js') }}"></script>
<script src="{{ asset('bundles/mybundle/js/admin/init.js') }}"></script>

Эти файлы собственно живут у нас по таком пути:

MyBundle/Resources/public/js/admin/jquery.filedrop.js
MyBundle/Resources/public/js/admin/js.fileDropBlock.js
MyBundle/Resources/public/js/admin/js.fileLoadByDefault.js
MyBundle/Resources/public/js/admin/js.init.js.

На заметку, что бы все было по верному, настойчиво рекомендую вам сделать в папке public папку admin в ней сделать файлы:

  1. js.fileDropBlock.js
  2. js.fileLoadByDefault.js
  3. init.js.

Как вы могли осознать 1-й файл из 4 это наш ранее загруженный с гита, так что не стесняемся и кидаем его в папку admin, а позже делаем assets. В консоле набираем

$ app/console assets:install www –symlink если все верно, то у вас в папке /bundle/mybundle/ должна возникнуть копия вашей папки public, возникла? Погнали дальше. Сейчас о всяком файле по порядку. Выходит, файл №1(js.fileDropBlock.js) и его код:

// MyBundle/Resources/public/js/admin/js.fileDropBlock.js

function fileDropBlock(block, type) {
    var allowType = {
        'img': ['image/jpeg', 'image/png', 'image/gif']
    };

    block.filedrop({
        url: '/upload-file', # url к которой будет протекать обращение при активизации загрузки
        paramname: 'file', # параметр. По сути это признак name вашего input поля
        fallbackid: 'upload_button', 
        maxfiles: 1, # кол-во файлов
        maxfilesize: 2, # размер файла в mb

# Реакция на ошибки. Здесь может быть что желательно
        error: function (err, file) {
            switch (err) {
                case 'BrowserNotSupported':
                    console.log('Old browser');
                    break;
                case 'FileTooLarge':
                    console.log('File Too Large');
                    break;
                case 'TooManyFiles':
                    console.log('Only 1 file can be downloader');
                    break;
                case 'FileTypeNotAllowed':
                    console.log('Wrong file type');
                    break;
                default:
                    console.log('Some error');
            }

        },
        allowedfiletypes: allowType[type], # разрешенные типы файлов для загрузки
        dragOver: function () {
            block.addClass('active-drag-block');
        },
        dragLeave: function () {
            block._removeClass('active-drag-block');
        },

        uploadFinished: function (i, file, response) {
            block.find('input[type="text"]').val(response.filePath); # в инпут разместим путь к файлу
        }
    })
}

файл №2(js.fileLoadByDefault.js) и его код:
// MyBundle/Resources/public/js/admin/js.LoadByDefault.js

var arrayType = {
    'img': [
        'image/png',
        'image/jpg',
        'image/jpeg'
    ],
    'pdf': [
        'application/pdf',
        'application/x-pdf'
    ]
};

function fileLoadByDefault(selector, type, block) {

    var input = document.getElementById(selector),
        formdata = false;
    input.click();
}

Не много, не правда ли? Он нам потребоваться чуть позднее. Выходит, и наконец файл под номером 3 (init.js) и его код:

// MyBundle/Resources/public/js/admin/init.js

(function ($) {
    $(document).ready(function () {
        $.fn.uploadFile = function (type) {
            var blockText = {
                'img': {'text': ['Drag Image File Here'], 'name': ['img'], 'id': ['imguploadform']}
            };

            this.append('<p>'   blockText[type].text   '</p>');
            this.append('<input type="file" name="'   type   'file" id="'   type   'uploadform" data-type="'  blockText[type].name  '">');
            this.addClass('drag_n_drop--'   type   'Path');
            $('input', this).hide();

            fileDropBlock(this, type);
        };

        var imgBlock = $('div', 'div[id$="_coverPath"]');
        imgBlock.uploadFile('img');

        $('input[type="file"]').on("change", function () {
            var $_this = $(this),
                type = $_this.data('type'),
                reader,
                file;
            file = this.files[0];

            if (window.FormData) {
                formdata = new FormData();
            }

            if (window.FileReader) {
                reader = new FileReader();
                reader.readAsDataURL(file);
            }

            if (formdata) {
                formdata.append("file", file);
            }

            if (!$.inArray(file.type, arrayType[type])) {
                $.ajax({
                    url: "/upload-file",
                    type: "POST",
                    data: formdata,
                    processData: false,
                    contentType: false,
                    success: function (res) {
                        var userData = jQuery.parseJSON(res);
$_this.parent().find('input[type="text"]').val(userData.filePath);
                    }
                });
            } else {
                alert('Wrong type')
            }
        });

        imgBlock.click(function () {
            fileLoadByDefault('imguploadform', 'img', this);
        });
    });
})(jQuery);

Давайте проясним что происходит и разберем код по частям. Ранее мы имели только один инпут на странице, а нам необходима область для Drag & Drop’a.

$.fn.uploadFile = function (type) {
    var blockText = {
        'img': {
            'text': ['Drag Image File Here'], 
            'name': ['img'], 
            'id': ['imguploadform']
        }
    };

    this.append('<p>'   blockText[type].text   '</p>');
    this.append('<input type="file" name="'   type   'file" id="'   type   'uploadform" data-type="'  blockText[type].name  '">');
    this.addClass('drag_n_drop--'   type   'Path');
    $('input', this).hide();

    fileDropBlock(this, type);
};

функция которая в качестве исключительного параметра принимает тип документа, тот, что мы ходим загружать (бросать) в определенную область.

Функция приняла его и вернула нам, по сути теснее новейший html код. Тот, что включает в себя абрац с текстом:

this.append('<p>'   blockText[type].text   '</p>');

Кнопку загрузки, она потом еще сыграет главную роль:

this.append('<input type="file" name="'   type   'file" id="'   type   'uploadform" data-type="'  blockText[type].name  '">');

Добавляем к элементу класс, что бы понимать какой он

this.addClass('drag_n_drop--'   type   'Path');

И скрываем все инпуты:

$('input', this).hide();

Добавим красок css:

// MyBundle/Resources/public/css/style.css

.drag_n_drop--imgPath{
    width: 150px;
    height: 100px;
    cursor: pointer;
    border: 2px solid #e0e0e0;
    background: #f9f9f9;
}

В финальном результате, позже всех таких манипуляций, у вас должно получиться что то схожее на это
image

Если так и есть, то все отлично.

Дальше по файлу init.js

Такой код:

var imgBlock = $('div', 'div[id$="_name"]'); # выбираем целый див в котором наш инпут
imgBlock.uploadFile('img'); # к выбранному применяем функцию uploadFile

Дальше вот такой маленький кусок кода:

$('input[type="file"]').on("change", function () { # реагируем на то, что в инпуте изменилось
    var $_this = $(this),
        type = $_this.data('type'),
        reader,
        file;
    file = this.files[0]; # ловим файл из инпута типа file

    if (window.FormData) {
        formdata = new FormData(); # берем данные (это новые плюшки в версии js)
    }

    if (window.FileReader) {
        reader = new FileReader(); # читаем файл
        reader.readAsDataURL(file);
    }

    if (formdata) {
        formdata.append("file", file); # присоедениям файл в объект
    }

    if (!$.inArray(file.type, arrayType[type])) { # проверяем что тип файла какой нам нужно
        $.ajax({
            url: "/upload-file", # идем по пути
            type: "POST", 
            data: formdata, # отправляем туда наши файлы
            processData: false,
            contentType: false,
            success: function (res) {
                var userData = jQuery.parseJSON(res); # парсим итог
                $_this.parent().find('input[type="text"]').val(userData.filePath); # находим наш спрятанный инпут и ставим в него путь к теснее закаченному файлу
            }
        });
    } else {
        alert('Wrong type'); # а напротив alert с оплошностью
    }
});

Дальше, код:

imgBlock.click(function () {
    fileLoadByDefault('imguploadform', 'img', this);
});

Здесь легко, когда кликаем по нашему диву, тот, что предуготовлен для бросания в него картинки, срабатывает функция fileLoadByDefault в ней 3 довода. 1 — id input’a с типом file. 2 — тип файла тот, что мы хотим загрузить. 3 — собственно сам эллемент родитель, по которому случился клик.

Собственно вот здесь, внимательный читатель мог подметить, что по сути наш код, реализует 2 метода загрузки. 1-й — Drag & Drop(то к чему и стримились), и 2-й — это клик по диву контейнера, для вызова стандартной формы upload’a файла, тот, что предуготовлен то для Drup&Drop. По сути 2 — это второстепенный результат, такой славный второстепенный результат.

Не хотелось бы вас огорчать, но мы проделали только половину работы… Дальше веселее, давайте сейчас покодим на php?!

Выходит, мы помним, что ссылаемся на ссылку [/upload-file] при любом событии, будь то Drop файла либо прямая загрузка.

Нужно нам определить роут для этого дела:

// MyFolder/MyBundle/Resourses/config/rounting.yml

my_file_upload:
    pattern:  /upload-file
    defaults: { _controller: MyFolderMyBundle:Default:uploadFile }

Взглянем на код способа [uploadFile]:

// MyFolder/MuBundle/Contraller/Default.php

<?php 
namespace MyFolderMyBundleController;

use SymfonyComponentHttpFoundationFileFile;
use SymfonyBundleFrameworkBundleControllerController;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentHttpFoundationFileUploadedFile;

class DefaultController extends Controller
{
    public function uploadFileAction()
    {

        $filename = $_FILES['file']; # принимает наш файл
        $uploadPath = $this->upload($filename); # запускаем функцию загрузки

        /**
          * Здесь думаю ясно. Обыкновенный результат на запрос
          */
        return null === $uploadPath
            ? new Response(json_encode(array(
                        'status' => 0,
                        'message' => 'Wrong file type'
                    )
                )
            )

            : new Response(json_encode(array(
                        'status' => 1,
                        'message' => $filename, # имя файла
                        'filePath' => $uploadPath # полный путь к нему
                    )
                )
            );
    }

    private function getFoldersForUploadFile($type)
    {
        $fileType = $this->returnExistFileType($type); #метод возвращающюй тип файлов которые дозволено грузить

        if ($fileType !== null) {
            return array(
                'root_dir' => $this->container->getParameter('upload_' . $fileType . '_root_directory'), # полный путь к папке с картинкой
                'dir' => $this->container->getParameter('upload_' . $fileType . '_directory'), # отосительный путь к папке
            );
        } else {
            return null;
        }
    }

    # способ возвращает ключ(тип) файла тот, что будет закачиваться
    private function returnExistFileType($type)
    {
        $typeArray = array(
            'img' => array(
                'image/png',
                'image/jpg',
                'image/jpeg',
            ),
            'pdf' => array(
                'application/pdf',
                'application/x-pdf',
            )
        );

        foreach ($typeArray as $key => $value) {
            if (in_array($type, $value)) {
                return $key;
            }
        }

        return null;
    }

    # Здесь собственно все и происходит. Загрузка, присвоение имени, перемещение в папку
    private function upload($file)
    {
        $filePath = $this->getFoldersForUploadFile($file['type']);

        if (null === $this->getFileInfo($file['name']) || $filePath === null) {

            return null;
        }
        $pathInfo = $this->getFileInfo($file['name']);
        $path = $this->fileUniqueName() . '.' . $pathInfo['extension'];
        $this->uploadFileToFolder($file['tmp_name'], $path, $filePath['root_dir']);

        unset($file);
        return $filePath['dir'] . DIRECTORY_SEPARATOR . $path;
    }

    # возвращает всю информацию о загруженном фале (что бы это не было)
    private function getFileInfo($file)
    {

        return $file !== null ? (array)pathinfo($file) : null;
    }

    # формирует неповторимое имя
    private function fileUniqueName()
    {

        return sha1(uniqid(mt_rand(), true));
    }

    # перемещает файл в нужную папку
    private function uploadFileToFolder($tmpFile, $newFileName, $rootFolder)
    {
        $e = new File($tmpFile);
        $e->move($rootFolder, $newFileName);
    }

Как оказалось в выводе, не так ужасен черт.

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB