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

4 5
Uploading files in Yii is done via the a form model, its validation rules and some controller code. Let's review what's
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 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 98
Instead of `model->load(...)`, we are using `UploadedFile::getInstance(...)`. [[\yii\web\UploadedFile|UploadedFile]] 
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]] that expects a file:
99

100
```php
101
$file instanceof UploadedFile || $file->error == UPLOAD_ERR_NO_FILE //in the code framework
102 103
```

104
If validation is successful, then we're saving the file: 
105

106 107 108 109
```php
$model->file->saveAs('uploads/' . $model->file->baseName . '.' . $model->file->extension);
```

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

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

114 115 116 117 118
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.
119

120
### Required
121

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

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

### MIME type

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

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

146
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`:
147 148 149 150 151

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

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

159
### Image properties
160 161

If you upload an image, [[yii\validators\ImageValidator|ImageValidator]] may come in handy. It verifies if an attribute
162
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).
163

164 165
Uploading multiple files
------------------------
166

Alexander Makarov committed
167
If you need to upload multiple files at once, some adjustments are required.
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
 
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!
        ];
    }
}
```
190 191

View:
192 193 194 195 196 197 198 199

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

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

200
<?= $form->field($model, 'file[]')->fileInput(['multiple' => true]) ?>
201 202 203 204 205 206

    <button>Submit</button>

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

207
The difference is the following line:
208

209
```php
210
<?= $form->field($model, 'file[]')->fileInput(['multiple' => true]) ?>
211
```
212

213
Controller:
214

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
```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) {
230 231 232 233 234
            $model->file = UploadedFile::getInstances($model, 'file');
            
            if ($model->file && $model->validate()) {
                foreach ($model->file as $file) {
                    $file->saveAs('uploads/' . $file->baseName . '.' . $file->extension);
235 236 237 238 239 240 241 242
                }
            }
        }

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

244 245 246
There are two differences from single file upload. First is that `UploadedFile::getInstances($model, 'file');` used
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.