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