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="MagentoConfigBlockSystemConfigFormFieldFile" 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 MagentoConfigBlockSystemConfigFormFieldFile

This is Magento’s default type for file upload. Keep in mind that image upload has a separate type: MagentoConfigBlockSystemConfigFormFieldImage. 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 MagentoConfigModelConfigBackendFile.

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>MagentoConfigModelConfigBackendFile</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="MagentoConfigBlockSystemConfigFormFieldFile" sortOrder="6" showInDefault="1" showInWebsite="1" >
            <label>Upload custom file</label>
            <backend_model>MagentoConfigModelConfigBackendFile</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 InchooCustomModelConfigBackend;
 
class CustomFileType extends MagentoConfigModelConfigBackendFile
{
    /**
     * @return string[]
     */
    public function getAllowedExtensions() {
        return ['csv', 'xls'];
    }
}

Change backend model in system.xml file to be InchooCustomModelConfigBackendCustomFileType (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.