2

I Created a form using uicomponent .

In Dynamicrows i have two element fileuploader And Simple textbox.

<dynamicRows ... >
    <field name="path">
        <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">path</item>
            <item name="dataScope" xsi:type="string">path</item>
            <item name="label" xsi:type="string" translate="true">Document</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">MyModule/file-preview</item>
            <item name="prefixName" xsi:type="string">option.order</item>
            <item name="prefixElementName" xsi:type="string">option_</item>
            <!--<item name="required" xsi:type="boolean">false</item>-->
            <item name="uploaderConfig" xsi:type="array">
                <item name="url" xsi:type="url" path="module/controller/action/field/path"/>
            </item>
        </item>
        </argument>
    </field>
    <field name="title">
        <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="dataType" xsi:type="string">text</item>
            <item name="label" xsi:type="string" translate="true">Document Title</item>
            <item name="formElement" xsi:type="string">input</item>
            <item name="source" xsi:type="string">title</item>
            <item name="sortOrder" xsi:type="number">10</item>
            <!--<item name="prefixElementName" xsi:type="string">txt_</item>-->
            <item name="dataScope" xsi:type="string">name</item>
            <item name="notice" xsi:type="string" translate="true">Enter Document Title</item>
            <item name="validation" xsi:type="array">
                <item name="required-entry" xsi:type="boolean">true</item>
            </item>
        </item>
        </argument>
    </field>
</dynamicRows>

Now When DynamicRows Are generating Fileupload Is Not Working .. i dont know what to pass in

<item name="uploaderConfig" xsi:type="array">
        <item name="url" xsi:type="url" path="module/controller/action/field/{HERE}"/>
</item>

File upload is working fine when fileuploader field is not generated using <dynamicrows>

In Working condition of fileuploading resoponse in controller is post response in this is name="test"

Array
(
    [test] => Array
        (
            [name] => Modules.odt
            [type] => application/vnd.oasis.opendocument.text
            [tmp_name] => /tmp/phpNeTkpQ
            [error] => 0
            [size] => 16705
        )

)

and using dynamicrows response is post response in this is name="formname[0][test]"

Array
(
    [docattchment] => Array
        (
            [name] => Array
                (
                    [0] => Array
                        (
                            [path] => Modules.odt
                        )

                )

            [type] => Array
                (
                    [0] => Array
                        (
                            [path] => application/vnd.oasis.opendocument.text
                        )

                )

            [tmp_name] => Array
                (
                    [0] => Array
                        (
                            [path] => /tmp/phpX8il5D
                        )

                )

            [error] => Array
                (
                    [0] => Array
                        (
                            [path] => 0
                        )

                )

            [size] => Array
                (
                    [0] => Array
                        (
                            [path] => 16705
                        )

                )

        )

)
0

5 Answers 5

3

Uploader requires array of the values name, type, tmp_name, error, size. So when we are using uploader in dynamic rows, we need to provide array of above values to uploader object. Try below

$docs = array();
$docattchments = isset($_FILES['docattchment']) ? $_FILES['docattchment'] : array();

foreach ($docattchments as $key => $docattchment){
    foreach ($docattchment as $doc){
        if($key == 'name'){
            $docs['name'] = $doc['image'];
        }
        if($key == 'type'){
            $docs['type'] = $doc['image'];
        }
        if($key == 'tmp_name'){
            $docs['tmp_name'] = $doc['image'];
        }
        if($key == 'error'){
            $docs['error'] = $doc['image'];
        }
        if($key == 'size'){
            $docs['size'] = $doc['image'];
        }
    }
}
if(!empty($docs)){
    $result = $this->imageUploader->saveFileToTmpDir($docs);
    $result['cookie'] = [
        'name' => $this->_getSession()->getName(),
        'value' => $this->_getSession()->getSessionId(),
        'lifetime' => $this->_getSession()->getCookieLifetime(),
        'path' => $this->_getSession()->getCookiePath(),
        'domain' => $this->_getSession()->getCookieDomain(),
    ];
}
1
  • I have a custom module that I added in the product add/edit form, how can I get image in plugin May 18, 2021 at 16:23
2

I solved it by changing my upload controller like that

<?php

namespace XXXXX\YYYYY\Controller\Adminhtml\News\Image;

use Magento\Framework\Controller\ResultFactory;

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

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

    public function execute()
    {
        try
        {
            $dynamicrows = $this->getRequest()->getFiles('tab4')['images']['images'];
            $files = array();
            foreach($dynamicrows as $key => $rows)
            {
                $files = $dynamicrows[$key]['image_url'];
            }

            $result = $this->imageUploader->saveFileToTmpDir($files);

            $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);
    }
}
1

i'm facing the same problem but not successfully uploading image inside dynamicRows with the help of your answer.


My Form

<fieldset name="tab4">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="label" xsi:type="string" translate="true">Images</item>
        </item>
    </argument>
    <dynamicRows name="images">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="componentType" xsi:type="string">dynamicRows</item>
                <item name="template" xsi:type="string">yyyyy/dynamic-rows/grid</item>
                <item name="addButtonLabel" xsi:type="string" translate="true">Ajouter image</item>
                <item name="deleteButtonLabel" xsi:type="string">Remove</item>
                <item name="columnsHeader" xsi:type="boolean">false</item>
                <item name="additionalClasses" xsi:type="array">
                    <item name="admin__field-wide" xsi:type="boolean">true</item>
                </item>
            </item>
        </argument>
        <container name="record">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="isTemplate" xsi:type="boolean">true</item>
                    <item name="is_collection" xsi:type="boolean">true</item>
                    <item name="component" xsi:type="string">Magento_Ui/js/dynamic-rows/record</item>
                    <item name="componentType" xsi:type="string">container</item>
                    <item name="positionProvider" xsi:type="string">image_position</item>
                </item>
            </argument>
            <field name="image_position">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="formElement" xsi:type="string">input</item>
                        <item name="source" xsi:type="string">news</item>
                        <item name="dataScope" xsi:type="string">image_position</item>
                        <item name="additionalClasses" xsi:type="array">
                            <item name="_hidden" xsi:type="boolean">true</item>
                        </item>
                    </item>
                </argument>
            </field>
            <field name="image_url">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="formElement" xsi:type="string">fileUploader</item>
                        <item name="template" xsi:type="string">yyyyy/uploader</item>
                        <item name="previewTmpl" xsi:type="string">yyyyy/image-preview</item>
                        <item name="source" xsi:type="string">news</item>
                        <item name="dataScope" xsi:type="string">image_url</item>
                        <item name="fit" xsi:type="boolean">true</item>
                        <item name="uploaderConfig" xsi:type="array">
                            <item name="url" xsi:type="url" path="yyyyy/news_image/upload"/>
                        </item>
                    </item>
                </argument>
            </field>
            <field name="image_legend">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="formElement" xsi:type="string">textarea</item>
                        <item name="source" xsi:type="string">news</item>
                        <item name="dataScope" xsi:type="string">image_legend</item>
                    </item>
                </argument>
            </field>
            <actionDelete>
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="componentType" xsi:type="string">actionDelete</item>
                        <item name="dataType" xsi:type="string">text</item>
                        <item name="fit" xsi:type="boolean">true</item>
                    </item>
                </argument>
            </actionDelete>
        </container>
    </dynamicRows>
</fieldset>

Upload Controller

<?php

namespace XXXXX\YYYYY\Controller\Adminhtml\News\Image;

use Magento\Framework\Controller\ResultFactory;

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

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

    public function execute()
    {
        try
        {
            $docs = array();
            $docattchments = isset($_FILES['image_url']) ? $_FILES['image_url'] : array();
            foreach($docattchments as $key => $docattchment)
            {
                foreach($docattchment as $doc)
                {
                    if($key == 'name')
                    {
                        $docs['name'] = $doc['images'];
                    }
                    if($key == 'type')
                    {
                        $docs['type'] = $doc['images'];
                    }
                    if($key == 'tmp_name')
                    {
                        $docs['tmp_name'] = $doc['images'];
                    }
                    if($key == 'error')
                    {
                        $docs['error'] = $doc['images'];
                    }
                    if($key == 'size')
                    {
                        $docs['size'] = $doc['images'];
                    }
                }
            }

            $result = $this->imageUploader->saveFileToTmpDir($docs);

            $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);
    }
}

Here is the error message

Attention

Notice: Undefined index: images][images][0][image_url in C:...\vendor\magento\framework\File\Uploader.php on line 554
1

I'm also facing the same problem on UI component fileUploader in Magento 2.3.2. The best way to handling dynamic fileUploader is extending core Upload class.

Below steps are solution for above problem,

Step 1: Have to create Uploder class in your custom module. For instance, below are example code,

File: app\code\Vendor\Module\File\DynamicFileUploader.php

<?php

namespace Vendor\Module\File;

use Magento\Framework\File\Uploader as CoreUploader;
use Magento\Framework\File\Mime;
use Vendor\Module\Model\DynamicImageUploader;
use Magento\Framework\App\ObjectManager;

class DynamicFileUploader extends CoreUploader
{
    /**
     * @var integer
     */
    const SINGLE_STYLE = 0;

    /**
     * @var integer
     */
    const MULTIPLE_STYLE = 1;

    /**
     * @param   mixed   $fileId
     * @param   Mime    $fileMime
     *
     * @throws \Exception
     */
    public function __construct(
        $fileId,
        Mime $fileMime = null
    ) {
        $this->_setUploadFileId($fileId);
        if (!file_exists($this->_file['tmp_name'])) {
            $code = empty($this->_file['tmp_name']) ? self::TMP_NAME_EMPTY : 0;
            throw new \Exception('The file was not uploaded.', $code);
        } else {
            $this->_fileExists = true;
        }
        $this->fileMime = $fileMime ?: ObjectManager::getInstance()->get(Mime::class);
    }

    /**
     * Set upload field id
     *
     * @param string|array $fileId
     * @return void
     * @throws \Exception
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
     */
    private function _setUploadFileId($fileId)
    {
        if (is_array($fileId)) {
            $this->_uploadType = self::MULTIPLE_STYLE;
            $this->_file = $fileId;
        } else {
            if (empty($_FILES)) {
                throw new \Exception('$_FILES array is empty');
            }

            preg_match("/^(.*?)\[(.*?)\]$/", $fileId, $file);

            if (is_array($file) && count($file) > 0 && !empty($file[0]) && !empty($file[1])) {
                array_shift($file);
                $this->_uploadType = self::MULTIPLE_STYLE;

                $fileAttributes = $_FILES[$file[0]];
                $tmpVar = [];

                foreach ($fileAttributes as $attributeName => $attributeValue) {
                    $tmpVar[$attributeName] = $attributeValue[$file[1]];
                }

                $fileAttributes = $tmpVar;
                $this->_file = $fileAttributes;
            } elseif (!empty($fileId) && isset($_FILES[$fileId])) {
                $this->_uploadType = self::SINGLE_STYLE;

                /* Custom Code Start: for dynamic file uploader */
                $fileInfo = [];
                $uploadFile = $_FILES[$fileId];
                foreach ($uploadFile as $key => $value) {
                    $keyValue = array_column($value, DynamicImageUploader::DYNAMIC_KEY_BANNER);
                    $fileInfo[$key] = is_array($keyValue) ? array_pop($keyValue) : "";
                }
                $this->_file = $fileInfo;
                /* Custom Code End */
            } elseif ($fileId == '') {
                throw new \Exception('Invalid parameter given. A valid $_FILES[] identifier is expected.');
            }
        }
    }
}

Note: The above file is extended with core file (Uploader.php) and added custom code, which will update the property ($this->_file) with expected format instead of array.

Step 2: Have to inject above class in DynamicImageUploader.php model class. For instance, below are example code.

File: app\code\Vendor\Module\Model\DynamicImageUploader.php

<?php

namespace Vendor\Module\Model;

use Vendor\Module\File\DynamicFileUploaderFactory;
use Magento\Framework\Filesystem;
use Magento\MediaStorage\Helper\File\Storage\Database as StorageDatabase;
use Magento\Store\Model\StoreManagerInterface;
use Psr\Log\LoggerInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\UrlInterface;
use Magento\Framework\Exception\LocalizedException;

class DynamicImageUploader
{
    /**
     * @var StorageDatabase
     */
    private $coreFileStorageDatabase;

    /**
     * @var string
     */
    private $mediaDirectory;

    /**
     * @var DynamicFileUploaderFactory
     */
    private $uploaderFactory;

    /**
     * @var StoreManagerInterface
     */
    private $storeManager;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var string
     */
    public $imageTmpPath;

    /**
     * @var string
     */
    public $imagePath;

    /**
     * @var array
     */
    public $allowedExtensions;

    /**
     * @var string
     */
    const DYNAMIC_KEY_BANNER = "banner";

    /**
     * @param   StorageDatabase             $coreFileStorageDatabase
     * @param   Filesystem                  $filesystem
     * @param   DynamicFileUploaderFactory  $uploaderFactory
     * @param   StoreManagerInterface       $storeManager
     * @param   LoggerInterface             $logger
     */
    public function __construct(
        StorageDatabase $coreFileStorageDatabase,
        Filesystem $filesystem,
        DynamicFileUploaderFactory $uploaderFactory,
        StoreManagerInterface $storeManager,
        LoggerInterface $logger
    ) {
        $this->coreFileStorageDatabase = $coreFileStorageDatabase;
        $this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA);
        $this->uploaderFactory = $uploaderFactory;
        $this->storeManager = $storeManager;
        $this->logger = $logger;
        $this->imageTmpPath = "image/tmp/banners";
        $this->imagePath = "image/banners";
        $this->allowedExtensions= ['jpg', 'jpeg', 'gif', 'png', 'svg'];
    }

    /**
     * Set allowed extensions.
     *
     * @param   array $allowedExtensions
     */
    public function setAllowedExtensions($allowedExtensions)
    {
        $this->allowedExtensions = $allowedExtensions;
    }

    /**
     * Get allowed extensions
     *
     * @return  array
     */
    public function getAllowedExtensions()
    {
        return $this->allowedExtensions;
    }

    /**
     * Get temp path for image.
     *
     * @return  string
     */
    public function getImageTmpPath()
    {
        return $this->imageTmpPath;
    }

    /**
     * Get base path for image.
     *
     * @return  string
     */
    public function getImagePath()
    {
        return $this->imagePath;
    }

    /**
     * Get file path.
     *
     * @param   string  $path
     * @param   string  $imageName
     *
     * @return  string
     */
    public function getFilePath($path, $imageName)
    {
        return rtrim($path, '/') . '/' . ltrim($imageName, '/');
    }

    /**
     * Move file from temp path to base path.
     *
     * @param   string  $imageName
     *
     * @return  string
     * @throws  LocalizedException
     */
    public function moveFileFromTmp($imageName)
    {
        $baseTmpPath = $this->getImageTmpPath();
        $basePath = $this->getImagePath();
        $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 LocalizedException(
                __('Something went wrong while saving the file(s).')
            );
        }

        return $imageName;
    }

    /**
     * Method to validate and save file to temp path.
     *
     * @param   mixed   $fileId
     *
     * @return  array
     * @throws  LocalizedException
     */
    public function saveFileToTmpDir($fileId)
    {
        $baseTmpPath = $this->getImageTmpPath();
        $uploader = $this->uploaderFactory->create(['fileId' => $fileId]);
        $uploader->setAllowedExtensions($this->getAllowedExtensions());
        $uploader->setAllowRenameFiles(true);
        $result = $uploader->save($this->mediaDirectory->getAbsolutePath($baseTmpPath));

        $result['tmp_name'] = str_replace('\\', '/', $result['tmp_name']);
        $result['path'] = str_replace('\\', '/', $result['path']);
        $result['url'] = sprintf(
            "%s%s",
            $this->storeManager->getStore()->getBaseUrl(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 LocalizedException(
                    __('Something went wrong while saving the file(s).')
                );
            }
        }

        return $result;
    }
}

Step 3: At final step, you have to confirm your Uploader controller file. For instance, below are example code.

File: app\code\Vendor\Module\Controller\Adminhtml\DynamicImage\Upload.php

<?php

namespace Vendor\Module\Controller\Adminhtml\DynamicImage;

use Magento\Framework\Controller\ResultFactory;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Vendor\Module\Model\DynamicImageUploader;

class Upload extends Action
{
    /**
     * @var DynamicImageUploader
     */
    public $imageUploader;

    /**
     * @param   Context                 $context
     * @param   DynamicImageUploader    $imageUploader
     */
    public function __construct(
        Context $context,
        DynamicImageUploader $imageUploader
    ) {
        parent::__construct($context);
        $this->imageUploader = $imageUploader;
    }

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

    public function execute()
    {
        try {
            $result = $this->imageUploader->saveFileToTmpDir('dynamic_rows_container_banner');
            $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);
    }
}
1
  • Hi @Aarief Mohideen Can you please upload your ui component (xml) file code! May 23, 2020 at 12:21
1

I also faced the same issue and found solution in some post. you need to override the core function _setUploadFileId() to fix this issue.

file path is vendor/magento/framework/File/Uploader.php

change _setUploadFileId() code with the below one and it will fix the issue for you :)

private function _setUploadFileId($fileId)
    {
        if (is_array($fileId)) {
            $this->_uploadType = self::MULTIPLE_STYLE;
            $this->_file = $fileId;
        } else {
            if (empty($_FILES)) {
                throw new \Exception('$_FILES array is empty');
            }

            preg_match("/^(.*?)\[(.*?)\]$/", $fileId, $file);

            if (is_array($file) && count($file) > 0 && !empty($file[0]) && (isset($file[1]) && !empty($file[1]))) {
                array_shift($file);
                $this->_uploadType = self::MULTIPLE_STYLE;

                $fileAttributes = $_FILES[$file[0]];
                $tmpVar = [];

                /*foreach ($fileAttributes as $attributeName => $attributeValue) {
                    $tmpVar[$attributeName] = $attributeValue[$file[1]];
                }*/
                foreach ($fileAttributes as $attributeName => $attributeValue) {
                    preg_match("/^(.*?)\](.*?)\$/", $file[1], $wasMultiArray);
                    if ( is_array($attributeValue) &&
                        !empty($wasMultiArray) &&
                        !empty($wasMultiArray[0])&&
                        !empty($wasMultiArray[1])) {
                        $imageName = "";
                        array_walk_recursive( $attributeValue,
                            function ( $item, $key ) use ( &$imageName )
                            {
                                $imageName = $item;
                            }
                        );
                        $tmpVar[$attributeName] = $imageName;
                    }else{
                        $tmpVar[$attributeName] = $attributeValue[$file[1]];
                    }
                }

                $fileAttributes = $tmpVar;
                $this->_file = $fileAttributes;
            } elseif (!empty($fileId) && isset($_FILES[$fileId])) {
                $this->_uploadType = self::SINGLE_STYLE;
                $this->_file = $_FILES[$fileId];
            } elseif ($fileId == '') {
                throw new \Exception('Invalid parameter given. A valid $_FILES[] identifier is expected.');
            }
        }
    }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.