upload-file.md 6.55 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
# Upload File with Yii2

### First you need to create a model that will handle the form of download the file.

```php
namespace app\models;

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

/**
 * UploadForm is the model behind the upload form.
 */
class UploadForm extends Model
{
    /**
     * @var UploadedFile|Null file attribute
     */
    public $file;

    /**
     * @return array the validation rules.
     */
    public function rules()
    {
        return [
            [['file'], 'file'],
        ];
    }
}
```
In this code, we created a model ```UploadForm``` with an attribute ```$file``` that will be is ```<input type="file">``` in upload form and pointed out to him validation rule ```file```. This rule is [[yii\validators\FileValidator|FileValidator]]

### Secondly create a view for our model.
```php
<?php
use yii\widgets\ActiveForm;

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

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

<button>Submit</button>

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

It is different attribute ```'enctype' => 'multipart/form-data'``` from the standard form. This value is required when you are using forms that have a file upload control. ```fileInput()``` represents a form input field.

### Thirdly, that create the controller that will connect our form and model.
```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');

            if ($model->validate()) {                
                $model->file->saveAs('uploads/' . $model->file->baseName . '.' . $model->file->extension);
            }
        }

        return $this->render('upload', ['model' => $model]);
    }
}
```
The difference here from the standard crud action, so use ```UploadedFile::getInstance(...)``` instead ```model->load(...)```. [[\yii\web\UploadedFile|UploadedFile]] does not run the model validation, it only provides information about the uploaded file. Therefore, you need to run validation manually ```$model->validate()```. This triggers the [[yii\validators\FileValidator|FileValidator]] that expects a file
```php
$file instanceof UploadedFile || $file->error == UPLOAD_ERR_NO_FILE //in code framework
```

If validation done without errors, then save the file 
```php
$model->file->saveAs('uploads/' . $model->file->baseName . '.' . $model->file->extension);
```
If you use "basic" application then forlder ```uploads``` should be create inside ```web``` folder.

Everything is ready, now run the page and download the file. Check the folder ```basic/web/uploads``` to make sure that you have downloaded.

## Additional information.

***

### Required rule

If you need to check the mandatory download the file, then use ```skipOnEmpty```.
```php
public function rules()
{
    return [
        [['file'], 'file', 'skipOnEmpty' => false],
    ];
}
```

***

### Path upload folder

Folder to download the file can be installed using ```Yii::getAlias('@app/uploads')```. This base path of currently running application and folder ```uploads``

***

### MIME type

FileValidator have property ```$types```
```php
public function rules()
{
    return [
        [['file'], 'file', 'types' => 'gif, jpg',],
    ];
}
```
it pulls
```php
in_array(strtolower(pathinfo($file->name, PATHINFO_EXTENSION)), $this->types, true))
```
As you can see, the name of the expansion may be one and the file type - other, actually.

``UploadedFile::getInstance()->type``` also do not take this value for granted.
Instead, use [[\yii\helpers\BaseFileHelper|FileHelper]] and his [[FileHelper::getMimeType()]] to determine the exact MIME type. 

If allowed to **load only the images**, using [[\yii\validators\ImageValidator|ImageValidator]] instead  [[yii\validators\FileValidator|FileValidator]].

```php
public function rules()
{
    return [
        [['file'], 'image', 'mimeTypes' => 'image/jpeg, image/png',],
    ];
}
```
```ImageValidator``` use use ```yii\helpers\FileHelper;``` for check mime types. 
[List Mime types](http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types)

***

### Multiple files uploader

If you need download multiple files, you will need to alter slightly the controller and view.
At first view:

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

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

if ($model->hasErrors()) { //it is necessary to see all the errors for all the files.
    echo '<pre>';
    print_r($model->getErrors());
    echo '</pre>';
}
?>

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

    <button>Submit</button>

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

In fact the only difference is in the one row.
```php
<?= $form->field($model, 'file[]')->fileInput(['multiple' => '']) ?>
```
instead
```php
<?= $form->field($model, 'file')->fileInput() ?>
```

* ```['multiple' => '']``` - HTML <input> multiple Attribute
* ```file[]``` vs ```file`` - need, otherwise UploadedFile sees only one file

We now turn to the controller
```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) {

            $files = UploadedFile::getInstances($model, 'file');

            foreach ($files as $file) {

                $_model = new UploadForm();

                $_model->file = $file;

                if ($_model->validate()) {
                    $_model->file->saveAs('uploads/' . $_model->file->baseName . '.' . $_model->file->extension);
                } else {
                    foreach ($_model->getErrors('file') as $error) {
                        $model->addError('file', $error);
                    }
                }
            }

            if ($model->hasErrors('file')){
                $model->addError(
                    'file',
                    count($model->getErrors('file')) . ' of ' . count($files) . ' files not uploaded'
                );
            }

        }

        return $this->render('upload', ['model' => $model]);
    }
}
```
Here the differences in:
* ``` UploadedFile::getInstances($model, 'file');``` instead ``` UploadedFile::getInstance($model, 'file');```. First returns **all** uploaded files for the given model attribute, second - one.
* All other differences follow from the first.