File upload in Magento 2 store configuration

One of the best parts in Magento is its admin interface. There is support for all kinds of input types (text fields, radio buttons, dropdowns, multiple selects, etc.), that can be stored in various ways (plain, encrypted, serialized), and displayed in numerous ways (grids, forms, simple fields, images).

What makes Magento great is the possibility to extend this default interface (especially store configuration part), and add our own fields easily. Let’s extend it with a custom file upload field.

Under Store > Configuration, we will find a lot of menus. Let’s add our custom one, under the Sales section. In the system.xml file in your module (etc/adminhtml/system.xml) we’ll have to create a new section called “custom_section”, a new group of fields called “custom_group” and a file upload field “custom_file_upload”.

Here is the code:

<section id="custom_section" translate="label" type="text" sortOrder="301" showInDefault="1" showInWebsite="1" showInStore="0">
    <label>Custom</label>
    <tab>sales</tab>
    <resource>Magento_Sales::config_sales</resource>
    <group id="custom_group" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" >
        <label>Custom group</label>
        <field id="custom_file_upload" translate="label" type="Magento\Config\Block\System\Config\Form\Field\File" sortOrder="6" showInDefault="1" showInWebsite="1" >
            <label>Upload custom file</label>
        </field>
    </group>
</section>

There are quite a few differences when compared to Magento 1: sort order, type and scope are now attributes of the section (or group, or field) instead of being child nodes. Section fields are self-explanatory. “tab” points to a place where we want our section. “sales” tab is created in Magento_Sales::etc/adminhtml/system.xml file so we can reference it here. “resource” is used for ACL. Only administrators with Magento_Sales::config_sales permission can access this section.

Group is the same as in Magento 1. In our group, we place a field that will allow us to upload the file. What specifics should it contain? “id” is ours to choose, but it needs to be unique per group. What matters the most is type. We see that it is set to Magento\Config\Block\System\Config\Form\Field\File

This is Magento’s default type for file upload. Keep in mind that image upload has a separate type: Magento\Config\Block\System\Config\Form\Field\Image. They’re almost the same, but not quite.

If we refresh the page, we get an upload file field. We can interact with it, even upload a file, but it is not working yet. We need to add a couple of things.

First, the backend_model. It is used to handle the process of uploading the file: setting the upload directory, checking allowed extensions, validating file size and saving the file path to database. Default backend model for file upload is Magento\Config\Model\Config\Backend\File.

We have to add one more thing to have a working file upload: <upload_dir> – upload directory.

Adding these two entries gives us a working file upload field:

<backend_model>Magento\Config\Model\Config\Backend\File</backend_model>
<upload_dir>upload</upload_dir>

Upload directory starts from application root. If we upload a file now, it will be placed in magento_root/upload/. Upload directory also has some interesting attributes. For example, adding a scope_info=”1″ to it will store the files based on the scope. If we upload a file under the default scope, its path will be magento_root/upload/default. Website 1 would give us magento_root/upload/websites/1/ etc.

Here is the final configuration:

<section id="custom_section" translate="label" type="text" sortOrder="301" showInDefault="1" showInWebsite="1" showInStore="0">
    <label>Custom</label>
    <tab>sales</tab>
    <resource>Magento_Sales::config_sales</resource>
    <group id="custom_group" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" >
        <label>Security</label>
        <field id="custom_file_upload" translate="label" type="Magento\Config\Block\System\Config\Form\Field\File" sortOrder="6" showInDefault="1" showInWebsite="1" >
            <label>Upload custom file</label>
            <backend_model>Magento\Config\Model\Config\Backend\File</backend_model>
            <upload_dir config="system" scope_info="1">test</upload_dir>
        </field>
    </group>
</section>

There are some other options we can add: frontend_model (eg. for a custom “drag and drop” file upload ), comment, tooltip, hint, validations etc. (you can check more in Magento_Config::etc/system_file.xsd).

In order to limit allowed file types, some changes are needed. First, we need to create our own backend model. Create a class in your module and extend the original backend model. Here is an example of such a class:

<?php
 
namespace Inchoo\Custom\Model\Config\Backend;
 
class CustomFileType extends \Magento\Config\Model\Config\Backend\File
{
    /**
     * @return string[]
     */
    public function getAllowedExtensions() {
        return ['csv', 'xls'];
    }
}

Change backend model in system.xml file to be \Inchoo\Custom\Model\Config\Backend\CustomFileType (or your class). By extending the getAllowedExtensions() method, we allow only .csv and .xls files to be uploaded.

And that is it. With only a few lines in configuration, we were able to create a fully working file upload button, with custom upload directory, and scope support.

You made it all the way down here so you must have enjoyed this post! You may also like:

Development environment for Magento 2 using Docker Tomas Novoselic
, | 0

Development environment for Magento 2 using Docker

Adding custom entries to admin system configuration Tomislav Nikcevski
, | 16

Adding custom entries to admin system configuration

Magento 2 custom widget Ivan Miskic
Ivan Miskic, | 9

Magento 2 custom widget

4 comments

  1. Thank you so much for the article! This was very helpful. A couple of quick comments on the backend_model:

    First:
    I am on Magento 2.2.5. On this version, the getAllowedExtensions method name begins with an underscore (_) and is a protected method.

    Second:
    I was uploading a file with sensitive data. This is obviously something that you don’t want to be public, so uploading to the default (media directory) wasn’t going to work. I was able to override the base upload path in the method _getUploadDir().

    My class is reflected below:

    <?php
    
    use Magento\Framework\App\Filesystem\DirectoryList;
    
    class MyBackendFileModel extends \Magento\Config\Model\Config\Backend\File
    {
        protected function _getAllowedExtensions()
        {
            return ['json'];
        }
    
        protected function _getUploadDir()
        {
            $this->_mediaDirectory = $this->_filesystem->getDirectoryWrite(DirectoryList::VAR_DIR);
            return parent::_getUploadDir();
        }
    }

    The Magento\Framework\App\Filesystem\DirectoryList has several constants referring to different directories within the Magento directory structure. Hopefully these tips are helpful to others.

    Inchoo has been an awesome source of Magento knowledge to me for many years – thank you again for the great article!

  2. Code works fine without any error message but I file is not uploading successfully. I can see file name in admin but actual file is not present at the location. What can be the issue here?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.