Magento – Magento 2 file upload issue – {“error”:”The file was not uploaded.”,”errorcode”:666}

file uploadimage-uploadmagento2

I've been fighting an issue with file/image uploading through my custom Catalog module. The upload field is in a programmatically generated UI Component form. Here is how my upload field is defined in my CustomOptions.php :

const FIELD_IMAGE_NAME = 'image';

static::FIELD_IMAGE_NAME => $this->getImageConfig(45),

protected function getImageConfig(int $sortOrder): array
{
    return [
        'arguments' => [
            'data' => [
                'config' => [
                    'label' => __('Image'),
                    'componentType' => 'field',
                    'dataScope' => static::FIELD_IMAGE_NAME,
                    'dataType' => 'string',
                    'source' => 'image',
                    'formElement' => 'imageUploader',
                    'sortOrder' => $sortOrder,
                    'elementTmpl' => 'ui/form/element/uploader/uploader',
                    'previewTmpl' => 'Vendor_Catalog/image-preview',
                    'uploaderConfig' => [
                        'url' => 'catalog/image/upload',
                    ],
                ],
            ],
        ],
    ];
}

My di.xml:

<type name="Vendor\Catalog\Model\ImageUploader">
    <arguments>
        <argument name="baseTmpPath" xsi:type="string">tmp/options/etchings</argument>
        <argument name="basePath" xsi:type="string">options/etchings</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>

Controller:

<?php

namespace Vendor\Catalog\Controller\Adminhtml\Image;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Vendor\Catalog\Model\ImageUploader;
use Magento\Framework\Controller\ResultFactory;

class Upload extends Action
{
    public $imageUploader;

    public function __construct(
        Context $context,
        ImageUploader $imageUploader
    ) {
        parent::__construct($context);
        $this->imageUploader = $imageUploader;
    }

    public function execute()
    {
        try {
            $result = $this->imageUploader->saveFileToTmpDir('image');
            $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);
    }
}

And my Model:

<?php

namespace Vendor\Catalog\Model;

use Exception;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Filesystem;
use Magento\Framework\UrlInterface;
use Magento\MediaStorage\Helper\File\Storage\Database;
use Magento\MediaStorage\Model\File\UploaderFactory;
use Magento\Store\Model\StoreManagerInterface;
use Psr\Log\LoggerInterface;

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

    public function __construct(
        Database $coreFileStorageDatabase,
        Filesystem $filesystem,
        UploaderFactory $uploaderFactory,
        StoreManagerInterface $storeManager,
        LoggerInterface $logger
    ) {
        $this->coreFileStorageDatabase = $coreFileStorageDatabase;
        $this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA);
        $this->uploaderFactory = $uploaderFactory;
        $this->storeManager = $storeManager;
        $this->logger = $logger;
        $this->baseTmpPath = 'tmp/options/etchings';
        $this->basePath = 'options/etchings';
        $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 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(
                    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;
    }
}

It's not a permissions issue. I've also tried using the Magento default ImageUploader with a virtualtype, it did not work either. Only result I get is a failure in the File/Uploader framework over here:

    if (!file_exists($this->_file['tmp_name'])) {
        $code = empty($this->_file['tmp_name']) ? self::TMP_NAME_EMPTY : 0;
        throw new \DomainException('The file was not uploaded.', $code);
    }

_file is always null.

Does anyone know what could possibly be wrong here?
I will greatly appreciate any suggestions, I am really backed up here.

Thanks,
Kristians

Best Answer

We experienced this issue when trying to upload large files for downloadable products.

It looks like there are a few causes for the same error code (666):

  • PHPs upload_max_filesize is smaller than the file being uploaded.
    You can validate this by checking phpinfo() from a web request.
    If this is the case you can update your PHP-FPM configuration with something like: upload_max_filesize=100M then restarting PHP-FPM.
  • The temp path being using to store the uploaded file is not writable (the related setting is upload_tmp_dir )