input-file-upload.md 6.25 KB
Newer Older
1 2
Uploading Files
===============
3

4
Uploading files in Yii is done via a form model, its validation rules and some controller code. Let's review what's
5
required to handle uploads properly.
6

7

8 9
Uploading single file
---------------------
10

11
First of all, you need to create a model that will handle file uploads. Create `models/UploadForm.php` with the following
12
content:
13 14 15 16 17 18 19 20 21 22 23 24 25

```php
namespace app\models;

use yii\base\Model;
use yii\web\UploadedFile;

/**
 * UploadForm is the model behind the upload form.
 */
class UploadForm extends Model
{
    /**
26
     * @var UploadedFile file attribute
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
     */
    public $file;

    /**
     * @return array the validation rules.
     */
    public function rules()
    {
        return [
            [['file'], 'file'],
        ];
    }
}
```

42
In the code above, we've created a model `UploadForm` with an attribute `file` that will become `<input type="file">` in
43 44
the HTML form. The attribute has the validation rule named `file` that uses [[yii\validators\FileValidator|FileValidator]].

45
### Form view
46

47
Next, create a view that will render the form:
48

49 50 51
```php
<?php
use yii\widgets\ActiveForm;
52
?>
53

54
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>
55 56 57 58 59

<?= $form->field($model, 'file')->fileInput() ?>

<button>Submit</button>

60
<?php ActiveForm::end() ?>
61 62
```

63
The `'enctype' => 'multipart/form-data'` is necessary because it allows file uploads. `fileInput()` represents a form
64
input field.
65

66
### Controller
67

68
Now create the controller that connects the form and the model together:
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86

```php
namespace app\controllers;

use Yii;
use yii\web\Controller;
use app\models\UploadForm;
use yii\web\UploadedFile;

class SiteController extends Controller
{
    public function actionUpload()
    {
        $model = new UploadForm();

        if (Yii::$app->request->isPost) {
            $model->file = UploadedFile::getInstance($model, 'file');

87
            if ($model->file && $model->validate()) {                
88 89 90 91 92 93 94 95
                $model->file->saveAs('uploads/' . $model->file->baseName . '.' . $model->file->extension);
            }
        }

        return $this->render('upload', ['model' => $model]);
    }
}
```
96

97
Instead of `model->load(...)`, we are using `UploadedFile::getInstance(...)`. [[\yii\web\UploadedFile|UploadedFile]] 
98 99
does not run the model validation, rather it only provides information about the uploaded file. Therefore, you need to run the validation manually via `$model->validate()` to trigger the [[yii\validators\FileValidator|FileValidator]]. The validator expects that
the attribute is an uploaded file, as you see in the core framework code:
100

101
```php
102 103 104
if (!$file instanceof UploadedFile || $file->error == UPLOAD_ERR_NO_FILE) {
    return [$this->uploadRequired, []];
}
105 106
```

107
If the validation is successful, then we're saving the file: 
108

109 110 111 112
```php
$model->file->saveAs('uploads/' . $model->file->baseName . '.' . $model->file->extension);
```

113
If you're using the "basic" application template, then folder `uploads` should be created under `web`.
114

115
That's it. Load the page and try uploading. Uploads should end up in `basic/web/uploads`.
116

117 118 119 120 121
Validation
----------

It's often required to adjust validation rules to accept certain files only or require uploading. Below we'll review
some common rule configurations.
122

123
### Required
124

125
If you need to make the file upload mandatory, use `skipOnEmpty` like the following:
126

127 128 129 130 131 132 133 134 135 136 137
```php
public function rules()
{
    return [
        [['file'], 'file', 'skipOnEmpty' => false],
    ];
}
```

### MIME type

138
It is wise to validate the type of file uploaded. FileValidator has the property `$extensions` for this purpose:
139

140 141 142 143
```php
public function rules()
{
    return [
Vladimir committed
144
        [['file'], 'file', 'extensions' => 'gif, jpg',],
145 146 147
    ];
}
```
148

149
Keep in mind that only the file extension will be validated, but not the actual file content. In order to validate the content as well, use the `mimeTypes` property of `FileValidator`:
150 151 152 153 154

```php
public function rules()
{
    return [
155
        [['file'], 'file', 'extensions' => 'jpg, png', 'mimeTypes' => 'image/jpeg, image/png',],
156 157 158 159
    ];
}
```

160 161
[List of common media types](http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types)

162
### Image properties
163 164

If you upload an image, [[yii\validators\ImageValidator|ImageValidator]] may come in handy. It verifies if an attribute
165
received a valid image that can be then either saved or processed using the [Imagine Extension](https://github.com/yiisoft/yii2/tree/master/extensions/imagine).
166

167 168
Uploading multiple files
------------------------
169

Alexander Makarov committed
170
If you need to upload multiple files at once, some adjustments are required.
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
 
Model:

```php
class UploadForm extends Model
{
    /**
     * @var UploadedFile|Null file attribute
     */
    public $file;

    /**
     * @return array the validation rules.
     */
    public function rules()
    {
        return [
            [['file'], 'file', 'maxFiles' => 10], // <--- here!
        ];
    }
}
```
193 194

View:
195 196 197 198 199 200 201 202

```php
<?php
use yii\widgets\ActiveForm;

$form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]);
?>

203
<?= $form->field($model, 'file[]')->fileInput(['multiple' => true]) ?>
204 205 206 207 208 209

    <button>Submit</button>

<?php ActiveForm::end(); ?>
```

210
The difference is the following line:
211

212
```php
213
<?= $form->field($model, 'file[]')->fileInput(['multiple' => true]) ?>
214
```
215

216
Controller:
217

218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
```php
namespace app\controllers;

use Yii;
use yii\web\Controller;
use app\models\UploadForm;
use yii\web\UploadedFile;

class SiteController extends Controller
{
    public function actionUpload()
    {
        $model = new UploadForm();

        if (Yii::$app->request->isPost) {
233 234 235 236 237
            $model->file = UploadedFile::getInstances($model, 'file');
            
            if ($model->file && $model->validate()) {
                foreach ($model->file as $file) {
                    $file->saveAs('uploads/' . $file->baseName . '.' . $file->extension);
238 239 240 241 242 243 244 245
                }
            }
        }

        return $this->render('upload', ['model' => $model]);
    }
}
```
246

247
There are two differences from single file upload. First is that `UploadedFile::getInstances($model, 'file');` is used
248 249
instead of `UploadedFile::getInstance($model, 'file');`. The former returns instances for **all** uploaded files while
the latter gives you only a single instance. The second difference is that we're doing `foreach` and saving each file.