Запрет скачивания файла по прямой ссылке в yii2

Рассмотрим один из способов предотвращения просмотра и скачивания файлов (документов) по прямой ссылке в yii2.

Для хранения статичных файлов, в том числе, загруженных изображений и аудио, зачастую используется общедоступные для просмотра папки. Естественно, эти файлы расположены в папке web, далее files, images и т.д. Т.е. зная url конкретного файла, мы можем его просмотреть и скачать по прямой ссылке. В случае, когда необходимо защитить файл от несанкционированного просмотра, следует его разместить вне папки web. Как известно, в yii2 папки, расположенные на уровне папки web закрыты от "посторонних глаз".

В качестве защищенной директории будем рассматривать папку runtime. Итак, приступим. Будем использовать расширение yii2-file-kit от trntv. В файл config.php в разделе components необходимо добавить следующие строки.

'fileStorage'=> [
   'class' => 'trntv\filekit\Storage',
   'baseUrl' => '@runtime/files',
   'filesystem'=> function() {
        $adapter = new \League\Flysystem\Adapter\Local('../runtime/files');
        return new League\Flysystem\Filesystem($adapter);
   }
],

В бекенде, в контроллере, который занимается добавлением материала, добавим actions

public function actions(){
    return [
            'uploadFile'=>[
                'class'=>'trntv\filekit\actions\UploadAction',
                'fileStorage' => 'fileStorage'
            ]
    ];
}

Соответственно, загрузка изображений будет осуществляться по URL: uploadFile. Подробнее об этом можно глянуть в статье "Загрузка файлов в yii2". В принципе, на этом этапе файл будет загружен и сохранен.

Теперь возникает задача выдачи файла на фронтенде. Поскольку, файл находится в "закрытой" директории, то прямые ссылки до файла из браузера не будут срабатывать. Обратимся к возможностям Yii::$app->response.

Если необходимо выдать небольшой (по объему) файл, то подойдет функция sendFile(), для выдачи объемных файлов следует использовать sendStreamAsFile().

public function actionDownload($id = null)
{
    if (!$id) {
        throw new NotFoundHttpException('File not found');
    }

    if (($model = Product::find()->where(['id' => $id])->one()) == NULL) {                    
        throw new NotFoundHttpException('File not found');
    }              

    if (($this->hasAccess(Yii::$app->user->id, $model->id))) {
        $path=Yii::$app->fileStorage->baseUrl .'/';               
        $f = fopen($path.$model->filename, 'r');
        \Yii::$app->response->sendStreamAsFile($f, $model->filename, ['mimeType' => '<mimeType>'])->send();
    } 
    else {
        throw new NotFoundHttpException('Access denied');
    }
    throw new NotFoundHttpException('Page not found1');
}

Функции проверки доступа (hasAccess($userId, $prouct)) зависят от конкретной задачи, это может быть доступ только авторизованным пользователям, либо доступ по id пользователя, естественно, что соответствие между id пользователя и id материала должно быть где-то зафиксировано.

{{ message }}

{{ 'Comments are closed.' | trans }}