{"id":186,"date":"2021-05-31T09:39:28","date_gmt":"2021-05-31T09:39:28","guid":{"rendered":"https:\/\/magebrew.com\/blog\/?p=186"},"modified":"2021-05-31T09:43:37","modified_gmt":"2021-05-31T09:43:37","slug":"magento-2-customizable-options-allow-any-file-upload-via-rest-api","status":"publish","type":"post","link":"https:\/\/magebrew.com\/blog\/magento-2-customizable-options-allow-any-file-upload-via-rest-api\/","title":{"rendered":"Magento 2 customizable options &#8211; allow any file upload via Rest API"},"content":{"rendered":"\n<p>Magento 2 custom options allows you to add file upload option to product. So on frontend customer is able to attach a file to product when placing an order.<\/p>\n\n\n\n<p>Recently I have discovered that it is not possible to upload file other than image via Rest API. In the code below I suggest a sample Plugin code that allows you to overcome this restriction.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>First declare plugin in app\/code\/Vendor\/ModuleName\/etc\/webapi_rest\/di.xml:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;?xml version=\"1.0\"?>\n\n&lt;config xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"\n        xsi:noNamespaceSchemaLocation=\"urn:magento:framework:ObjectManager\/etc\/config.xsd\">\n    &lt;type name=\"Magento\\Catalog\\Model\\Webapi\\Product\\Option\\Type\\File\\Processor\">\n        &lt;plugin name=\"AllowFileUploadPlugin\"\n                type=\"Vendor\\ModuleName\\Plugin\\Catalog\\Webaip\\ProductOptionTypeFileProcessor\\AroundProcessFileContent\\AllowFileUploadPlugin\"\/>\n    &lt;\/type>\n&lt;\/config><\/pre>\n\n\n\n<p>Plugin code can look like this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;?php\n\ndeclare(strict_types=1);\n\nnamespace Vendor\\ModuleName\\Plugin\\Catalog\\Webaip\\ProductOptionTypeFileProcessor\\AroundProcessFileContent;\n\nuse Magento\\Catalog\\Model\\Webapi\\Product\\Option\\Type\\File\\Processor;\nuse Magento\\Framework\\Api\\Data\\ImageContentInterface;\nuse Magento\\Framework\\Api\\Uploader;\nuse Magento\\Framework\\App\\Filesystem\\DirectoryList;\nuse Magento\\Framework\\Exception\\FileSystemException;\nuse Magento\\Framework\\Exception\\InputException;\nuse Magento\\Framework\\Filesystem;\nuse Magento\\Framework\\Filesystem\\Directory\\WriteInterface;\nuse Magento\\Framework\\Phrase;\n\n\/**\n * Allow not only images to be uploaded\n *\n * Class AllowFileUploadPlugin\n *\/\nclass AllowFileUploadPlugin\n{\n\n    public const FILE_EXTENSIONS_TO_PROCESS = [\n        'pdf',\n        'docx'\n    ];\n\n    \/**\n     * @var string\n     *\/\n    protected $destinationFolder = 'custom_options\/quote';\n\n    \/**\n     * 4Mb file size limit\n     *\/\n    public const FILE_SIZE_LIMIT_BYTES = 4 * 1048576;\n\n    \/**\n     * @var Filesystem\n     *\/\n    private $filesystem;\n\n    \/**\n     * @var Uploader\n     *\/\n    private $uploader;\n\n    \/**\n     * @var WriteInterface\n     *\/\n    private $mediaDirectory;\n\n    \/**\n     * AllowFileUploadPlugin constructor.\n     * @param Filesystem $filesystem\n     * @param Uploader $uploader\n     * @throws FileSystemException\n     *\/\n    public function __construct(\n        Filesystem $filesystem,\n        Uploader $uploader\n    ) {\n        $this->filesystem = $filesystem;\n        $this->uploader = $uploader;\n        $this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA);\n    }\n\n    \/**\n     * Core magento allows only images to be uploaded\n     * Here we allow PDF and DOC files\n     *\n     * @param Processor $subject\n     * @param callable $proceed\n     * @param ImageContentInterface $fileContent\n     * @return array\n     * @throws InputException\n     * @throws FileSystemException\n     * @SuppressWarnings(PHPMD.UnusedFormalParameter)\n     *\/\n    public function aroundProcessFileContent(\n        Processor $subject,\n        callable $proceed,\n        ImageContentInterface $fileContent\n    ): array {\n        $fileMatches = array_search(\n            strtolower($fileContent->getType()),\n            array_map('strtolower', self::FILE_EXTENSIONS_TO_PROCESS),\n            true\n        ) !== false;\n\n        if (!$fileMatches) {\n            return $proceed($fileContent);\n        }\n\n        $filePath = $this->saveFile($fileContent);\n\n        $fileAbsolutePath = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA)->getAbsolutePath($filePath);\n        $fileHash = md5($this->filesystem->getDirectoryRead(DirectoryList::MEDIA)->readFile($filePath));\n        $imageSize = getimagesize($fileAbsolutePath);\n        $result = [\n            'type'       => $fileContent->getType(),\n            'title'      => $fileContent->getName(),\n            'fullpath'   => $fileAbsolutePath,\n            'quote_path' => $filePath,\n            'order_path' => $filePath,\n            'size'       => filesize($fileAbsolutePath),\n            'width'      => $imageSize ? $imageSize[0] : 0,\n            'height'     => $imageSize ? $imageSize[1] : 0,\n            'secret_key' => substr($fileHash, 0, 20),\n        ];\n\n        return $result;\n    }\n\n    \/**\n     * Save file\n     *\n     * @param ImageContentInterface $fileContent\n     * @return string\n     * @throws InputException\n     * @throws FileSystemException\n     *\/\n    private function saveFile(ImageContentInterface $fileContent): string\n    {\n        $this->validateSize($fileContent);\n\n        $content = @base64_decode($fileContent->getBase64EncodedData(), true);\n        $tmpDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::SYS_TMP);\n        $fileName = $this->getFileName($fileContent);\n        $tmpFileName = substr(md5((string)mt_rand()), 0, 7) . '.' . $fileName;\n        $tmpDirectory->writeFile($tmpFileName, $content);\n\n        $fileAttributes = [\n            'tmp_name' => $tmpDirectory->getAbsolutePath() . $tmpFileName,\n            'name'     => $fileContent->getName()\n        ];\n\n        $this->uploader->processFileAttributes($fileAttributes);\n        $this->uploader->setFilesDispersion(true);\n        $this->uploader->setFilenamesCaseSensitivity(false);\n        $this->uploader->setAllowRenameFiles(true);\n        $this->uploader->save($this->mediaDirectory->getAbsolutePath($this->destinationFolder), $fileName);\n\n        $filePath = $this->uploader->getUploadedFileName();\n\n        return $this->destinationFolder . $filePath;\n    }\n\n    \/**\n     * Validate file size\n     *\n     * @param ImageContentInterface $fileContent\n     * @throws InputException\n     *\/\n    private function validateSize(ImageContentInterface $fileContent): void\n    {\n        \/\/convert base 64 size to real one\n        $fileSizeInBytes = strlen($fileContent->getBase64EncodedData()) \/ 4 * 3;\n\n        if ($fileSizeInBytes > self::FILE_SIZE_LIMIT_BYTES) {\n            throw new InputException(new Phrase('File size exceeds the limit'));\n        }\n    }\n\n    \/**\n     * Get file name\n     *\n     * @param $fileContent\n     * @return mixed|string\n     *\/\n    private function getFileName($fileContent)\n    {\n        $fileName = $fileContent->getName();\n        if (!pathinfo($fileName, PATHINFO_EXTENSION)) {\n            $fileName .= '.' . $fileContent->getType();\n        }\n\n        return $fileName;\n    }\n}\n<\/pre>\n\n\n\n<p>Hope it is helpful.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Magento 2 custom options allows you to add file upload option to product. So on frontend customer is able to attach a file to product when placing an order. Recently I have discovered that it is not possible to upload file other than image via Rest API. In the code below I suggest a sample &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/magebrew.com\/blog\/magento-2-customizable-options-allow-any-file-upload-via-rest-api\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Magento 2 customizable options &#8211; allow any file upload via Rest API&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[19],"tags":[20],"_links":{"self":[{"href":"https:\/\/magebrew.com\/blog\/wp-json\/wp\/v2\/posts\/186"}],"collection":[{"href":"https:\/\/magebrew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/magebrew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/magebrew.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/magebrew.com\/blog\/wp-json\/wp\/v2\/comments?post=186"}],"version-history":[{"count":7,"href":"https:\/\/magebrew.com\/blog\/wp-json\/wp\/v2\/posts\/186\/revisions"}],"predecessor-version":[{"id":194,"href":"https:\/\/magebrew.com\/blog\/wp-json\/wp\/v2\/posts\/186\/revisions\/194"}],"wp:attachment":[{"href":"https:\/\/magebrew.com\/blog\/wp-json\/wp\/v2\/media?parent=186"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/magebrew.com\/blog\/wp-json\/wp\/v2\/categories?post=186"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/magebrew.com\/blog\/wp-json\/wp\/v2\/tags?post=186"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}