<p>For anyone wanting to do this in Craft 3 there are a couple changes to the code you need to add to getSettingsHtml().</p>

<pre><code>'myImageSelectConfig' =&gt; array(
            'id'                 =&gt; 'myImage',
            'name'               =&gt; 'myImage',
            'jsClass'            =&gt; 'Craft.AssetSelectInput',
            'elementType'        =&gt; 'craft\\elements\\Asset',
            'elements'           =&gt; $this-&gt;getSettings()['myImage'] &amp;&amp; count($this-&gt;getSettings()['myImage']) ? [Craft::$app-&gt;getElements()-&gt;getElementById($this-&gt;getSettings()['myImage'][0])] : null,
            'sources'            =&gt; array('folder:2'),
            'criteria'           =&gt; array('kind' =&gt; array('image'), 'localeEnabled' =&gt; null),
            'limit'              =&gt; 1,
            'viewMode'           =&gt; 'table',
            'selectionLabel'     =&gt; Craft::t('app','Add image'),
        )
</code></pre>

CC BY-SA 4.0
2018-05-24T21:05:26.230Z
2018-05-24T21:05:26.230Z
Bradlay Law
<p>Unfortunately, a simple HTML file input on its own isn't enough to upload an image (in Craft or anywhere else) – the input simply exposes the file on your local filesystem to the browser/server. In other words, with your current setup, you're not actually uploading anything.</p>

<p>In my opinion, you have two choices:</p>

<ol>
<li>Use Assets and Craft's built-in Assets ElementSelect input (easy)</li>
<li>Don't use Assets; upload the image natively using a file input (harder)</li>
</ol>

<p>Personally, I'd recommend that unless you <em>can't</em> have the image be an Asset file for some reason, you should definitely go with the Assets approach. It's way easier to set up, offers a familiar user experience and you can even use image transforms and whatnot since your <code>myImage</code> file will be a bona fide Asset.</p>

<p>Either way, here's how option 1 could look:</p>

<p>In your plugin's <code>settings.html</code> file, replace your current call to the <code>forms.file()</code> macro with this:</p>

<pre><code>{{ forms.field({
  label: "My Image"
}, include('_includes/forms/elementSelect', myImageSelectConfig)) }}
</code></pre>

<p>Then, fire up your plugin's primary class. Revise your <code>getSettingsHtml()</code> method to include the <code>myImageSelectConfig</code> attribute referenced in <code>settings.html</code> above, so the method looks something like this:</p>

<pre><code>public function getSettingsHtml()
{
    return craft()-&gt;templates-&gt;render('yourpluginhandle/settings', array(
        'settings' =&gt; $this-&gt;getSettings(),
        'myImageSelectConfig' =&gt; array(
            'id'                 =&gt; 'myImage',
            'name'               =&gt; 'myImage',
            'jsClass'            =&gt; 'Craft.AssetSelectInput',
            'elementType'        =&gt; new ElementTypeVariable(craft()-&gt;elements-&gt;getElementType(ElementType::Asset)),
            'elements'           =&gt; $this-&gt;getSettings()['myImage'] ? craft()-&gt;elements-&gt;getCriteria(ElementType::Asset, array('id' =&gt; $this-&gt;getSettings()['myImage'])) : null,
            'sources'            =&gt; array('folder:2'),
            'criteria'           =&gt; array('kind' =&gt; array('image'), 'localeEnabled' =&gt; null),
            'limit'              =&gt; 1,
            'viewMode'           =&gt; 'table',
            'selectionLabel'     =&gt; Craft::t('Add image'),
        ),
    ));
}
</code></pre>

<p>Note: The <code>sources</code> attribute refers to the ID(s) for the Asset folder the admin should upload images to (and choose images from). You can look at the <code>craft_assetfolders</code> database table to figure out which ID(s) you want. Alternatively you can remove the <code>sources</code> property entirely, to enable all folders – which is probably the best option if this is going to be a publicly available plugin.</p>

<p>And that's it. The actual <code>myImage</code> setting value stored will be an array with the ID to the selected Asset file, e.g. <code>{"myImage":["42"]}</code>. If you need to pull the Asset in a template, you'll need to add a <a href="http://buildwithcraft.com/docs/plugins/variables" rel="noreferrer">Variable</a> class to your plugin, exposing the plugin settings:</p>

<pre><code>class MyPluginVariable
{
    public function settings()
    {
        return craft()-&gt;plugins-&gt;getPlugin('myPlugin')-&gt;getSettings();
    }
}
</code></pre>

<p>...and in your template:</p>

<pre><code>{% set assetId = craft.myPlugin.settings.myImage %}
{% set asset = craft.assets.id(assetId).first() %}
</code></pre>

<p><strong>If you don't want to go with the Assets approach</strong>, you can keep your <code>forms.file()</code> macro in <code>settings.html</code>. However, there's a few more things you'll need to handle to get the upload working.</p>

<p>First, in order to actually upload a file from a file input, the enclosing form needs to have an <code>enctype="multipart/form-data"</code> attribute. Unfortunately, plugin settings forms don't, and there's no real way to add it. However, you can use jQuery to hack it in – add this to your plugin's <code>settings.html</code> file:</p>

<pre><code>{% includeJs '$("form:first").attr("enctype", "multipart/form-data");' %}
</code></pre>

<p>Second, you'll need some server side code to actually handle the upload. Add the following to your plugin's primary class:</p>

<pre><code>public function prepSettings($settings)
{

    $imageFile = UploadedFile::getInstanceByName('settings.myImage');

    if ($imageFile) {

        // Where should the image be stored?
        $basePath = $_SERVER['DOCUMENT_ROOT'];
        $uploadFolder = '/uploads/yourpluginhandle/';
        $filePath = $basePath.$uploadFolder.$imageFile-&gt;name;

        // Make sure the upload folder exists
        IOHelper::ensureFolderExists($basePath.$uploadFolder);

        // Attempt to upload the file
        if ($imageFile-&gt;saveAs($filePath)) {
            // File uploaded successfully – store the relative filepath in the plugin settings
            $settings['myImage'] = str_replace($basePath, '', $filePath);
        } else {
            // Aw.
            craft()-&gt;userSession-&gt;setError(Craft::t('Unable to upload image {filename}', array(
                'filename' =&gt; $imageFile-&gt;name,
            )));
        }
    }

    return $settings;

}
</code></pre>

<p>The above example is pretty much the bare minimum you'd need in order to upload a file from your plugin's settings page, to an arbitrary folder on your server's file system. The example <code>prepSettings()</code> script will upload the file to a folder called <code>/uploads/yourpluginhandle</code> in the public webroot (i.e. <code>$_SERVER['DOCUMENT_ROOT']</code>), before storing the <em>relative</em> path to the file in the settings, if the upload was successful.</p>

<p>Keep in mind that file uploading is actually fairly complex (lots of things can go wrong), and also a potential security risk. You should probably consider adding some code to ensure that the file's mime type (<code>$imageFile-&gt;type</code>), size (<code>$imageFile-&gt;size</code>) and other properties are legit, before calling <code>$file-&gt;saveAs()</code>. You might also need to create a UI for displaying and deleting the current image, and making sure old and no longer selected files are deleted from the server.</p>

<p>Finally, I'd posit that a vanilla browser file input is a pretty terrible user experience, compared to the Assets select input in Craft proper. There's all sorts of JavaScript libraries to help out with that, of course – in any case the above is hopefully a good starting point if you go down this road.</p>

CC BY-SA 3.0
2016-08-14T02:03:14.333Z
Seriously, though... I want to fly to Norway just to buy you a beer.
Brad Bell
CC BY-SA 3.0
2016-08-14T05:54:10.850Z
@BradBell That's fortunate - I love beer!
Mats Mikkel Rummelhoff
CC BY-SA 3.0
2016-08-14T14:32:03.307Z
No worries, @BillyMedia – hope it helps you out.
Mats Mikkel Rummelhoff
CC BY-SA 3.0
2016-08-13T18:41:09.100Z
2016-08-13T18:46:34.880Z
2016-08-13T18:46:34.880Z
Mats Mikkel Rummelhoff
<p>My plugin needs to allow admin to upload an image. I have the field setup and saving ok. But i cant seem to control the upload location. </p>

<p>This is what i have so far in my settings:</p>

<pre><code>{{ forms.file({
    label: "My Image"|t,
    instructions: ""|t,
    name: 'myImage',
    value: settings.myImage
}) }}
</code></pre>

<p>When I output the <code>settings.myImage</code>, I get the file name on its own and no path. I would like it to upload to my <code>/uploads</code> folder.
So for example, the image I uploaded was "test.jpg". When I output the result of <code>settings.myImage</code>, I get "test.jpg" and no path.</p>

<plugin-development><plugins><settings>
Plugin settings - file field upload location
# Plugin settings - file field upload location

(Asked by: username_0 on 2016-06-09)

My plugin needs to allow admin to upload an image. I have the field setup and saving ok. But i cant seem to control the upload location. 
This is what i have so far in my settings:
{{ forms.file({
    label: "My Image"|t,
    instructions: ""|t,
    name: 'myImage',
    value: settings.myImage
}) }}

When I output the settings.myImage, I get the file name on its own and no path. I would like it to upload to my /uploads folder.
So for example, the image I uploaded was "test.jpg". When I output the result of settings.myImage, I get "test.jpg" and no path.

**Tags:** plugin-development, plugins, settings


---

## 2 answers

### Answer by username_2 on 2016-08-13. Score: 10 | Accepted answer

Unfortunately, a simple HTML file input on its own isn't enough to upload an image (in Craft or anywhere else) – the input simply exposes the file on your local filesystem to the browser/server. In other words, with your current setup, you're not actually uploading anything.
In my opinion, you have two choices:

Use Assets and Craft's built-in Assets ElementSelect input (easy)
Don't use Assets; upload the image natively using a file input (harder)

Personally, I'd recommend that unless you can't have the image be an Asset file for some reason, you should definitely go with the Assets approach. It's way easier to set up, offers a familiar user experience and you can even use image transforms and whatnot since your myImage file will be a bona fide Asset.
Either way, here's how option 1 could look:
In your plugin's settings.html file, replace your current call to the forms.file() macro with this:
{{ forms.field({
  label: "My Image"
}, include('_includes/forms/elementSelect', myImageSelectConfig)) }}

Then, fire up your plugin's primary class. Revise your getSettingsHtml() method to include the myImageSelectConfig attribute referenced in settings.html above, so the method looks something like this:
public function getSettingsHtml()
{
    return craft()->templates->render('yourpluginhandle/settings', array(
        'settings' => $this->getSettings(),
        'myImageSelectConfig' => array(
            'id'                 => 'myImage',
            'name'               => 'myImage',
            'jsClass'            => 'Craft.AssetSelectInput',
            'elementType'        => new ElementTypeVariable(craft()->elements->getElementType(ElementType::Asset)),
            'elements'           => $this->getSettings()['myImage'] ? craft()->elements->getCriteria(ElementType::Asset, array('id' => $this->getSettings()['myImage'])) : null,
            'sources'            => array('folder:2'),
            'criteria'           => array('kind' => array('image'), 'localeEnabled' => null),
            'limit'              => 1,
            'viewMode'           => 'table',
            'selectionLabel'     => Craft::t('Add image'),
        ),
    ));
}

Note: The sources attribute refers to the ID(s) for the Asset folder the admin should upload images to (and choose images from). You can look at the craft_assetfolders database table to figure out which ID(s) you want. Alternatively you can remove the sources property entirely, to enable all folders – which is probably the best option if this is going to be a publicly available plugin.
And that's it. The actual myImage setting value stored will be an array with the ID to the selected Asset file, e.g. {"myImage":["42"]}. If you need to pull the Asset in a template, you'll need to add a Variable class to your plugin, exposing the plugin settings:
class MyPluginVariable
{
    public function settings()
    {
        return craft()->plugins->getPlugin('myPlugin')->getSettings();
    }
}

...and in your template:
{% set assetId = craft.myPlugin.settings.myImage %}
{% set asset = craft.assets.id(assetId).first() %}

If you don't want to go with the Assets approach, you can keep your forms.file() macro in settings.html. However, there's a few more things you'll need to handle to get the upload working.
First, in order to actually upload a file from a file input, the enclosing form needs to have an enctype="multipart/form-data" attribute. Unfortunately, plugin settings forms don't, and there's no real way to add it. However, you can use jQuery to hack it in – add this to your plugin's settings.html file:
{% includeJs '$("form:first").attr("enctype", "multipart/form-data");' %}

Second, you'll need some server side code to actually handle the upload. Add the following to your plugin's primary class:
public function prepSettings($settings)
{

    $imageFile = UploadedFile::getInstanceByName('settings.myImage');

    if ($imageFile) {

        // Where should the image be stored?
        $basePath = $_SERVER['DOCUMENT_ROOT'];
        $uploadFolder = '/uploads/yourpluginhandle/';
        $filePath = $basePath.$uploadFolder.$imageFile->name;

        // Make sure the upload folder exists
        IOHelper::ensureFolderExists($basePath.$uploadFolder);

        // Attempt to upload the file
        if ($imageFile->saveAs($filePath)) {
            // File uploaded successfully – store the relative filepath in the plugin settings
            $settings['myImage'] = str_replace($basePath, '', $filePath);
        } else {
            // Aw.
            craft()->userSession->setError(Craft::t('Unable to upload image {filename}', array(
                'filename' => $imageFile->name,
            )));
        }
    }

    return $settings;

}

The above example is pretty much the bare minimum you'd need in order to upload a file from your plugin's settings page, to an arbitrary folder on your server's file system. The example prepSettings() script will upload the file to a folder called /uploads/yourpluginhandle in the public webroot (i.e. $_SERVER['DOCUMENT_ROOT']), before storing the relative path to the file in the settings, if the upload was successful.
Keep in mind that file uploading is actually fairly complex (lots of things can go wrong), and also a potential security risk. You should probably consider adding some code to ensure that the file's mime type ($imageFile->type), size ($imageFile->size) and other properties are legit, before calling $file->saveAs(). You might also need to create a UI for displaying and deleting the current image, and making sure old and no longer selected files are deleted from the server.
Finally, I'd posit that a vanilla browser file input is a pretty terrible user experience, compared to the Assets select input in Craft proper. There's all sorts of JavaScript libraries to help out with that, of course – in any case the above is hopefully a good starting point if you go down this road.

> **Comments:**
> **username_3**: Seriously, though... I want to fly to Norway just to buy you a beer.  
> **username_2**: @BradBell That's fortunate - I love beer!  
> **username_2**: No worries, @username_0 – hope it helps you out.  

---

### Answer by username_1 on 2018-05-24. Score: 1

For anyone wanting to do this in Craft 3 there are a couple changes to the code you need to add to getSettingsHtml().
'myImageSelectConfig' => array(
            'id'                 => 'myImage',
            'name'               => 'myImage',
            'jsClass'            => 'Craft.AssetSelectInput',
            'elementType'        => 'craft\\elements\\Asset',
            'elements'           => $this->getSettings()['myImage'] && count($this->getSettings()['myImage']) ? [Craft::$app->getElements()->getElementById($this->getSettings()['myImage'][0])] : null,
            'sources'            => array('folder:2'),
            'criteria'           => array('kind' => array('image'), 'localeEnabled' => null),
            'limit'              => 1,
            'viewMode'           => 'table',
            'selectionLabel'     => Craft::t('app','Add image'),
        )


---

