Displaying uploaded file from DB for update with FileInput widget

  1. Understanding Basics
  2. Displaying Uploaded File for Update

This is for folks who are using \kartik\widgets\FileInput to upload files from your client. How do you display an image for update after you have uploaded it to the server using the widget?

Note: The FileInput plugin has been upgraded to v1.5.0 since this wiki is written. With this release, it allows you to set an initial preview and caption of files (for update scenarios). Refer usage demo or plugin docs and plugin demos.

Understanding Basics

The FileInput widget is an enhancement of the HTML5 file input. It offers you the ability to preview your selected files from the client before it is uploaded to the server. It also supports a method to upload single or multiple files from the client to the server. However, this process occurs only one way, as this is limited by the HTML5 file input capability. Hence the file input only pushes/uploads files from client to server and not vice-versa. Therefore, it does not offer a method to fetch a preselected value from the server and display on the client. Hence, once the file is uploaded to the server, you cannot display the uploaded file directly from the file input.

Displaying Uploaded File for Update

Here are the steps for you to show the uploaded file considering a scenario for User profile image upload.

Step 1: Setup Model

You have a User model and an attribute image_file that stores the file name of the uploaded image file.

Step 2: Setup Upload Access Paths

You have set the images to be uploaded to a location $baseUrl/uploads where $baseUrl is the application baseUrl.

Step 3: Get Display Image Function

Create a simple function in your User model like this

const IMAGE_PLACEHOLDER = '/images/default_user.jpg';

public function getDisplayImage() {
    if (empty($model->image_file)) {
        // if you do not want a placeholder
        $image = null;

        // else if you want to display a placeholder
        $image = Html::img(self::IMAGE_PLACEHOLDER, [
            'alt'=>Yii::t('app', 'No avatar yet'),
            'title'=>Yii::t('app', 'Upload your avatar by selecting browse below'),
            'class'=>'file-preview-image'
            // add a CSS class to make your image styling consistent
        ]);
    }
    else {
        $image = Html::img(Yii::$app->urlManager->baseUrl . '/' . $model->image_file, [
            'alt'=>Yii::t('app', 'Avatar for ') . $model->username,
            'title'=>Yii::t('app', 'Click remove button below to remove this image'),
            'class'=>'file-preview-image'
            // add a CSS class to make your image styling consistent
        ]);
    }

    // enclose in a container if you wish with appropriate styles
    return ($image == null) ? null : 
        Html::tag('div', $image, ['class' => 'file-preview-frame']); 
}
Step 4: Delete Image Function

In your model write your function to delete and remove an uploaded image.

public function deleteImage() {
    $image = Yii::$app->basePath . '/uploads/' . $this->image_file;
    if (unlink($image)) {
        $this->image_file = null;
        $this->save();
        return true;
    }
    return false;
}
Step 5: Your view/form

Here is how you should render the view/form where you have the file input widget:

// profile.php
// display your image or a placeholder first
echo $model->imageDisplay;

if (empty($model->image_file)) {
    echo $form->field($model, 'image_file')->widget(FileInput::classname(), [
        'options' => ['accept' => 'image/*'],
    ]);
}
else {
    echo Html::a(
        Yii::t('app', 'Remove Image'), 
        Url::to['/user/deleteImage', 'id'=>$model->id],
        ['class' => 'btn btn-danger']
    );
}
Step 6: Your controller

Include the file removal action in your controller that redirects back to the profile view above

// UserController.php

// NOTE: You must set controller access rules for this action
// below to allow only specific user(s) to delete the image
public function actionDeleteImage($id) {
    $model = User::findOne($id);
    if ($model->deleteImage()) {
        Yii::$app->session->setFlash('success', 
       'Your image was removed successfully. Upload another by clicking Browse below');
    } else {
        Yii::$app->session->setFlash('error', 
       'Error removing image. Please try again later or contact the system admin.');
    }
    return $this->render('profile', ['model'=>$model]);
}