Monday, 22 February 2016

Upload file in Yii 2 using FileInput widget

Model


namespace common\models;

use yii\db\ActiveRecord;

/**
* Class Person
* @package common\models
* @package common\models
* @property int $id unique person identifier
* @property string $name person / user name
* @property array $avatar generated filename on server
* @property string $filename source filename from client
*/
class Person extends ActiveRecord
{
    /**
    * @var mixed image the attribute for rendering the file input
    * widget for upload on the form
    */
    public $image;

    public function rules()
    {
        return [
            [['image'], 'safe'],
            [['image'], 'file', 'extensions'=>'jpg, gif, png'],
        ];
    }
}

Controller

The upload path for your files is important. Its recommended you save it in a global parameter like Yii::$app->params['uploadPath']. For example:
Yii::$app->params['uploadPath'] = Yii::$app->basePath . '/uploads/';
Next setup your controller action for upload.
use Yii;
use common\models\Person;
use yii\web\UploadedFile;

class PersonController extends \yii\web\Controller
{
    public function actionCreate()
    {
        $model = new Person;
        if ($model->load(Yii::$app->request->post())) {
            // get the uploaded file instance. for multiple file uploads
            // the following data will return an array
            $image = UploadedFile::getInstance($model, 'image');

            // store the source file name
            $model->filename = $image->name;
            $ext = end((explode(".", $image->name)));

            // generate a unique file name
            $model->avatar = Yii::$app->security->generateRandomString().".{$ext}";

            // the path to save file, you can set an uploadPath
            // in Yii::$app->params (as used in example below)
            $path = Yii::$app->params['uploadPath'] . $model->avatar;

            if($model->save()){
                $image->saveAs($path);
                return $this->redirect(['view', 'id'=>$model->_id]);
            } else {
                // error in saving model
            }
        }
        return $this->render('create', [
            'model'=>$model,
        ]);
    }
}

View


Edit your _form.php or your other view file where you are rendering the create and image upload form.
use kartik\widgets\ActiveForm; // or yii\widgets\ActiveForm
use kartik\widgets\FileInput;
// or 'use kartik\file\FileInput' if you have only installed 
// yii2-widget-fileinput in isolation

$form = ActiveForm::begin([
    'options'=>['enctype'=>'multipart/form-data'] // important
]);
echo $form->field($model, 'filename');

// your fileinput widget for single file upload
echo $form->field($model, 'image')->widget(FileInput::classname(), [
    'options'=>['accept'=>'image/*'],
    'pluginOptions'=>['allowedFileExtensions'=>['jpg','gif','png']
]);

/**
* uncomment for multiple file upload
*
echo $form->field($model, 'image[]')->widget(FileInput::classname(), [
    'options'=>['accept'=>'image/*', 'multiple'=>true],
    'pluginOptions'=>['allowedFileExtensions'=>['jpg','gif','png']
]);
*
*/
echo Html::submitButton($model->isNewRecord ? 'Upload' : 'Update', [
    'class'=>$model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']
);
ActiveForm::end();
And thus, you should now be able to check your uploaded files in the path as set in Yii::$app->params['uploadPath']. In case you are using multiple file input, the $image variable in controller will return an array of files, which you must loop through. Its important for multiple file uploads to follow an array naming convention as mentioned in this web tip.

5 comments: