Magento 2: Implemente UI Fileuploader

22

Recientemente implementé el componente de interfaz de usuario FileUploader en mi formulario en Magento 2.1.7.

El código para esto está aquí ( app / code / Vendor / Blog / view / adminhtml / ui_component / vendor_blog_form.xml ):

<field name="featured_images">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="label" translate="true" xsi:type="string">Hervorgehobene Bilder:</item>
                    <item name="formElement" xsi:type="string">fileUploader</item>
                    <item name="componentType" xsi:type="string">fileUploader</item>
                    <item name="previewTmpl" xsi:type="string">Magento_Catalog/image-preview</item>
                    <item name="elementTmpl" xsi:type="string">ui/form/element/uploader/uploader</item>
                    <item name="allowedExtensions" xsi:type="string">jpg jpeg gif png</item>
                    <item name="notice" xsi:type="string" translate="true">Erlaubte Dateitypen: png, gif, jpg, jpeg.</item>
                    <item name="maxFileSize" xsi:type="number">2097152</item>
                    <item name="source" xsi:type="string">blog</item>
                    <item name="sortOrder" xsi:type="number">10</item>
                    <item name="dataScope" xsi:type="string">featured_images</item>
                    <item name="validation" xsi:type="array">
                        <item name="required-entry" xsi:type="boolean">false</item>
                    </item>
                    <item name="uploaderConfig" xsi:type="array">
                        <item name="url" xsi:type="url" path="vendor_blog/blog/upload"/>
                    </item>
                </item>
            </argument>
        </field>

Mi controlador para esto es este ( app / code / Vendor / Blog / Controller / Adminhtml / Blog / Upload.php ):

<?php

namespace Vendor\Blog\Controller\Adminhtml\Blog;

use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Backend\App\Action;  

class Upload extends \Vendor\Blog\Controller\Adminhtml\Blog
{

    protected $_fileUploaderFactory;
    protected $_directory_list;
    protected $_logger;

    public function __construct(
        Action\Context $context,
        \Magento\Framework\Registry $coreRegistry,
        \Magento\MediaStorage\Model\File\UploaderFactory $fileUploaderFactory,
        \Magento\Framework\App\Filesystem\DirectoryList $directory_list,
        \Psr\Log\LoggerInterface $logger
    ) {
        $this->_fileUploaderFactory = $fileUploaderFactory;
        $this->_directory_list = $directory_list;
        $this->_logger = $logger;
        parent::__construct($context, $coreRegistry);
    }

    public function execute(){
        $uploader = $this->_fileUploaderFactory->create(['fileId' => 'featured_images']);
        $uploader->setAllowedExtensions(['jpg', 'jpeg', 'gif', 'png']);
        $uploader->setAllowRenameFiles(false);
        $uploader->setFilesDispersion(false);
        $path = $this->_filesystem->getDirectoryRead(DirectoryList::MEDIA)->getAbsolutePath('blog');
        //$path = $this->_directory_list->getPath('media') . '/blog';
        $this->_logger->debug('Uploader.php: '.$path);
        $uploader->save($path);
    }
}

Sin embargo, cuando subo una imagen e inspecciono la llamada en la consola de Chrome, aparece un error 500 con excepción: la matriz $ _FILES está vacía .

Estoy luchando durante dos días, pero no puedo hacer que funcione correctamente. Cuando descomento la $pathlínea de variable alternativa , la carga se realiza correctamente pero no obtengo una vista previa.

Leí que podría ser el enctypeformulario que está causando el problema, pero no encontré ninguna información sobre cómo verificar esto para un formulario de Componente de IU.

Si necesita el código de excepción completo, hágamelo saber.

Agradezco toda ayuda posible. ¡Gracias!

Hallleron
fuente
¿Por qué no intentas otra forma de carga? por ej. webkul.com/blog/…
Pallavi
Lamentablemente, estoy limitado a usar solo los componentes UI puros con declaración XML para esta extensión. Sin embargo, sería una buena alternativa.
hallleron

Respuestas:

35

Sigo estos pasos para agregar el componente UI fileuploader en forma de administrador

Utilizo el componente UI fileuploader para cargar un icono para mi extensión de preguntas frecuentes. Puede tomar referencia desde aquí: https://github.com/mageprince/magento2-FAQ

1) Agregar campo en admin_form.xml(Formulario de administrador)

<field name="icon">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="dataType" xsi:type="string">string</item>
            <item name="source" xsi:type="string">FaqGroup</item>
            <item name="label" xsi:type="string" translate="true">Group Image</item>
            <item name="visible" xsi:type="boolean">true</item>
            <item name="formElement" xsi:type="string">fileUploader</item>
            <item name="elementTmpl" xsi:type="string">ui/form/element/uploader/uploader</item>
            <item name="previewTmpl" xsi:type="string">Vendor_Module/image-preview</item>
            <item name="required" xsi:type="boolean">false</item>
            <item name="sortOrder" xsi:type="number">40</item>
            <item name="uploaderConfig" xsi:type="array">
                <item name="url" xsi:type="url" path="your_router/faqgroup/upload"/>
            </item>
        </item>
    </argument>
</field>

2) Ahora necesitamos crear un controlador que definimos en uploaderConfigforma de administrador:<item name="url" xsi:type="url" path="vendor_module/faqgroup/upload"/>

app / code / Vendor / Module / Controller / Adminhtml / FaqGroup / Upload.php

<?php

namespace Vendor\Module\Controller\Adminhtml\FaqGroup;

use Magento\Framework\Controller\ResultFactory;

class Upload extends \Magento\Backend\App\Action
{
    public $imageUploader;

    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Vendor\Module\Model\ImageUploader $imageUploader
    ) {
        parent::__construct($context);
        $this->imageUploader = $imageUploader;
    }

    public function _isAllowed()
    {
        return $this->_authorization->isAllowed('Vendor_Module::Faq');
    }

    public function execute()
    {
        try {
            $result = $this->imageUploader->saveFileToTmpDir('icon');
            $result['cookie'] = [
                'name' => $this->_getSession()->getName(),
                'value' => $this->_getSession()->getSessionId(),
                'lifetime' => $this->_getSession()->getCookieLifetime(),
                'path' => $this->_getSession()->getCookiePath(),
                'domain' => $this->_getSession()->getCookieDomain(),
            ];
        } catch (\Exception $e) {
            $result = ['error' => $e->getMessage(), 'errorcode' => $e->getCode()];
        }
        return $this->resultFactory->create(ResultFactory::TYPE_JSON)->setData($result);
    }
}

3) Crear ImageUploader.php

app / code / Vendor / Module / Model / ImageUploader.php

<?php

namespace Prince\Faq\Model;

class ImageUploader
{
    private $coreFileStorageDatabase;
    private $mediaDirectory;
    private $uploaderFactory;
    private $storeManager;
    private $logger;
    public $baseTmpPath;
    public $basePath;
    public $allowedExtensions;

    public function __construct(
        \Magento\MediaStorage\Helper\File\Storage\Database $coreFileStorageDatabase,
        \Magento\Framework\Filesystem $filesystem,
        \Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Psr\Log\LoggerInterface $logger
    ) {
        $this->coreFileStorageDatabase = $coreFileStorageDatabase;
        $this->mediaDirectory = $filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::MEDIA);
        $this->uploaderFactory = $uploaderFactory;
        $this->storeManager = $storeManager;
        $this->logger = $logger;
        $this->baseTmpPath = "faq/tmp/icon";
        $this->basePath = "faq/icon";
        $this->allowedExtensions= ['jpg', 'jpeg', 'gif', 'png'];
    }

    public function setBaseTmpPath($baseTmpPath)
    {
        $this->baseTmpPath = $baseTmpPath;
    }

    public function setBasePath($basePath)
    {
        $this->basePath = $basePath;
    }

    public function setAllowedExtensions($allowedExtensions)
    {
        $this->allowedExtensions = $allowedExtensions;
    }

    public function getBaseTmpPath()
    {
        return $this->baseTmpPath;
    }

    public function getBasePath()
    {
        return $this->basePath;
    }

    public function getAllowedExtensions()
    {
        return $this->allowedExtensions;
    }

    public function getFilePath($path, $imageName)
    {
        return rtrim($path, '/') . '/' . ltrim($imageName, '/');
    }

    public function moveFileFromTmp($imageName)
    {
        $baseTmpPath = $this->getBaseTmpPath();
        $basePath = $this->getBasePath();
        $baseImagePath = $this->getFilePath($basePath, $imageName);
        $baseTmpImagePath = $this->getFilePath($baseTmpPath, $imageName);
        try {
            $this->coreFileStorageDatabase->copyFile(
                $baseTmpImagePath,
                $baseImagePath
            );
            $this->mediaDirectory->renameFile(
                $baseTmpImagePath,
                $baseImagePath
            );
        } catch (\Exception $e) {
            throw new \Magento\Framework\Exception\LocalizedException(
                __('Something went wrong while saving the file(s).')
            );
        }
        return $imageName;
    }

    public function saveFileToTmpDir($fileId)
    {
        $baseTmpPath = $this->getBaseTmpPath();
        $uploader = $this->uploaderFactory->create(['fileId' => $fileId]);
        $uploader->setAllowedExtensions($this->getAllowedExtensions());
        $uploader->setAllowRenameFiles(true);
        $result = $uploader->save($this->mediaDirectory->getAbsolutePath($baseTmpPath));
        if (!$result) {
            throw new \Magento\Framework\Exception\LocalizedException(
                __('File can not be saved to the destination folder.')
            );
        }

        $result['tmp_name'] = str_replace('\\', '/', $result['tmp_name']);
        $result['path'] = str_replace('\\', '/', $result['path']);
        $result['url'] = $this->storeManager
                ->getStore()
                ->getBaseUrl(
                    \Magento\Framework\UrlInterface::URL_TYPE_MEDIA
                ) . $this->getFilePath($baseTmpPath, $result['file']);
        $result['name'] = $result['file'];
        if (isset($result['file'])) {
            try {
                $relativePath = rtrim($baseTmpPath, '/') . '/' . ltrim($result['file'], '/');
                $this->coreFileStorageDatabase->saveFile($relativePath);
            } catch (\Exception $e) {
                $this->logger->critical($e);
                throw new \Magento\Framework\Exception\LocalizedException(
                    __('Something went wrong while saving the file(s).')
                );
            }
        }
        return $result;
    }
}

4) Crear image-preview.html

app / code / Vendor / Module / view / adminhtml / web / template / image-preview.html

<div class="file-uploader-summary">
    <div class="file-uploader-preview">
        <a attr="href: $parent.getFilePreview($file)" target="_blank">
            <img
                class="preview-image"
                tabindex="0"
                event="load: $parent.onPreviewLoad.bind($parent)"
                attr="
                    src: $parent.getFilePreview($file),
                    alt: $file.name">
        </a>

        <div class="actions">
            <button
                type="button"
                class="action-remove"
                data-role="delete-button"
                attr="title: $t('Delete image')"
                click="$parent.removeFile.bind($parent, $file)">
                <span translate="'Delete image'"/>
            </button>
        </div>
    </div>

    <div class="file-uploader-filename" text="$file.name"/>
    <div class="file-uploader-meta">
        <text args="$file.previewWidth"/>x<text args="$file.previewHeight"/>
    </div>
</div>

5) Ahora agregue argumentos para ImageUploader.phpen di.xml

app / code / Vendor / Module / etc / di.xml

<type name="Vendor\Module\Model\ImageUploader">
    <arguments>
        <!-- Temporary file stored in pub/media/faq/tmp/icon -->
        <argument name="baseTmpPath" xsi:type="string">faq/tmp/icon</argument>
        <argument name="basePath" xsi:type="string">faq/icon</argument>
        <argument name="allowedExtensions" xsi:type="array">
            <item name="jpg" xsi:type="string">jpg</item>
            <item name="jpeg" xsi:type="string">jpeg</item>
            <item name="gif" xsi:type="string">gif</item>
            <item name="png" xsi:type="string">png</item>
        </argument>
    </arguments>
</type>

Verifique este archivo para cargar la imagen cargada en el formulario de edición: DataProvider.php

SALIDA:

ingrese la descripción de la imagen aquí

Para guardar la imagen en la base de datos

app / code / Vendor / Module / Controller / Adminhtml / Save.php

<?php

namespace Vendor\Module\Controller\Adminhtml;

use Magento\Framework\Exception\LocalizedException;

class Save extends \Magento\Backend\App\Action
{
    protected $dataPersistor;

    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Framework\App\Request\DataPersistorInterface $dataPersistor
    ) {
        $this->dataPersistor = $dataPersistor;
        parent::__construct($context);
    }

    public function execute()
    {
        ...
        ...
        $data = $this->_filterFoodData($data);
        $model->setData($data);
        $model->save();
        ...
        ...     
    }

    public function _filterFoodData(array $rawData)
    {
        //Replace icon with fileuploader field name
        $data = $rawData;
        if (isset($data['icon'][0]['name'])) {
            $data['icon'] = $data['icon'][0]['name'];
        } else {
            $data['icon'] = null;
        }
        return $data;
    }
}

Para mostrar la imagen cargada en la página de edición de formulario:

app / code / Vendor / Module / Model / DataProvider.php

<?php

namespace Vendor\Module\Model;

use Magento\Store\Model\StoreManagerInterface;

class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider
{
    ...
    ...

    public function getData()
    {
        ...
        ...
        $items = $this->collection->getItems();

        //Replace icon with fileuploader field name
        foreach ($items as $model) {
            $this->loadedData[$model->getId()] = $model->getData();
            if ($model->getIcon()) {
                $m['icon'][0]['name'] = $model->getIcon();
                $m['icon'][0]['url'] = $this->getMediaUrl().$model->getIcon();
                $fullData = $this->loadedData;
                $this->loadedData[$model->getId()] = array_merge($fullData[$model->getId()], $m);
            }
        }
        ...
        ...

        return $this->loadedData;
    }

    public function getMediaUrl()
    {
        $mediaUrl = $this->storeManager->getStore()
            ->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA).'faq/tmp/icon/';
        return $mediaUrl;
    }
}
Príncipe Patel
fuente
Muchas gracias por esto, realmente lo aprecio. Sin embargo, cuando implemento su solución y subo una imagen, mi respuesta devuelta en la consola de Chrome es: {"error": "$ _ FILES array está vacío", "errorcode": 0}. El archivo no se ha subido (verifiqué chmod) y, por supuesto, la vista previa no puede aparecer.
hallleron
1
En realidad, lo hice funcionar finalmente con la ayuda de su código. ¡Así que muchas gracias! ¡Eres mi héroe! :-)
hallleron
De nada :)
Prince Patel
@PrincePatel esto es genial, pero suponiendo que tenga un formulario de edición, ¿cómo puedo hacer que la plantilla de vista previa de la imagen se lea desde el getData del proveedor de datos
Yehia A.Salam
1
@PrincePatel Estoy trabajando en Magento 2.3 y llamando a "ImageUploader" desde di.xml y enviando los parámetros de baseTmpPath, basePath y allowExtensions desde el mismo. Ahora mi modelo "ImageUploader" arroja un error "Excepción # 0 (BadMethodCallException): Falta el argumento requerido $ baseTmpPath". ¿Pueden ayudarme a administrar desde di.xml en lugar de establecer estáticamente en la función de constructor del modelo "ImageUploader"?
Dhara Bhatti
6

Suplemento para el componente Magento 2.2 UI

Compare con Magento 2.1, en Magento 2.2 , el componente UI tenía algunas diferencias opcionales como las siguientes. Podríamos hacer uso del Magento_Catalog/image-previewtemplo oficial como vista previa, y el resto de códigos como controlador podrían referirse a la respuesta aceptada .

<field name="image" formElement="fileUploader">
    <settings>
        <notice translate="true">Allowed file types: jpg, jpeg, gif, png.</notice>
        <label translate="true">Image</label>
        <componentType>fileUploader</componentType>
    </settings>
    <formElements>
        <fileUploader>
            <settings>
                <allowedExtensions>jpg jpeg gif png</allowedExtensions>
                <maxFileSize>10240000</maxFileSize>
                <placeholderType>image</placeholderType>
                <previewTmpl>Magento_Catalog/image-preview</previewTmpl>
                <uploaderConfig>
                    <param xsi:type="string" name="url">path/to/controller</param>
                </uploaderConfig>
            </settings>
        </fileUploader>
    </formElements>
</field>
Key Shang
fuente
1
Me sale el error TypeError: value.map is not a function. ¿Cómo puedo solucionarlo?
Nero Phung
@NeroPhung Hola, prueba esta solución magento.stackexchange.com/q/138642/44237
Key Shang
He hecho este problema por mí mismo al seguir esa publicación. ¡Gracias por el apoyo!
Nero Phung
@KeyShang, en su código, ¿Cómo y dónde pongo la validación para el campo de carga de imágenes?
Jaisa
@Sri veo tu pregunta, la responderé en tu pregunta magento.stackexchange.com/questions/211957/… , dame algo de tiempo.
Key Shang