img
tags and links to download the file. There is nothing suitable in the extensions . The closest in meaning is the upload extension, but it also does not provide a number of necessary functions. So I decided to write myself. Right before the publication of the article, I accidentally saw a recipe , in which a similar idea was considered. // class MyModel extends CActiveRecord { public $image; public function rules(){ return array( array('image', 'file', 'types'=>'jpg, gif, png'), ); } } // class MyModelController extends CController { public function actionCreate(){ $modMyModel=new MyModel; if(isset($_POST['MyModel'])){ $modMyModel->attributes=$_POST['MyModel']; $modMyModel->id_image=CUploadedFile::getInstance($modMyModel,'image'); if($modMyModel->save()){ $modMyModel->id_image->saveAs('path/to/localFile'); // , // } } $this->render('create', array('model'=>$modMyModel)); } } // <?php echo CHtml::form('','post',array('enctype'=>'multipart/form-data')); ?> ... <?php echo CHtml::activeFileField($modMyModel, 'image'); ?> ... <?php echo CHtml::endForm(); ?>
actionUpdate()
, because it waits for the file to be loaded with each call. And it would be convenient to work with files as with ordinary properties - load when creating a model and, if necessary, reload when it changes.path/to/localFile
can be stored in the model property..../protected/data/files
best place to store .../protected/data/files
. Generally speaking, for files created in the course of work, there is a folder .../protected/runtime
, but according to the meaning of the data
directory is more suitable for these purposes. The file name will be generated randomly ( uniqid()
) and stored in the model property $modMyModel->id_image
, in the following paragraphs I will tell you how. True, this approach has one pitfall - the data
directory is closed for accessing from the browser. How to deal with this - a little later. Looking ahead, I propose to upload files dynamically via readfile()
, and to publish pictures (more precisely, thumbnails of pictures) in the assets
folder. <?php echo $modMyModel->id_image ?> <?php echo CHtml::activeFileField($modMyModel, 'id_image_file'); ?>
id_image_file
. Instead of echo $modMyModel->id_image
you can insert a link to download the file or a thumbnail image.$_FILES
with the name id_image_file
. If so, create a CUploadedFile
object and write it to $modMyModel->id_image
. Then validate $modMyModel->id_image
standard way. To do this, create your own validator DFileValidator
, inherited from CFileValidator
. And immediately another one - DImageValidator
, inherited from DFileValidator
, in which we specify the default file types for images.$modMyModel->id_image
, moreover in the form of CUploadedFile
. In order for the download not to be tied to a specific property, you need to check before saving the model whether any of its properties are objects of the CUploadedFile
class, and if they are, load them and save the addresses. Now the model will look like this: class MyModel extends DActiveRecord { public $id_image; public function rules(){ return array( array('id_image', 'DImageValidator'), ); } public function beforeSave() { foreach ($this->attributes as $key => $attribute) if ($attribute instanceof CUploadedFile) { $strSource = uniqid(); if ($attribute->saveAs(Yii::getPathOfAlias('application.data.files') . '/' . $strSource)) $this->$key = $strSource; } return parent::beforeSave(); } }
.../protected/data/files
with random names.rules()
model, assign a validator DFileValidator
to this property.'_file'
to it.actionCreate()
and actionUpdate()
methods can be left unchanged.src
tag parameter img
? How to give a file to download? In my opinion, it would be convenient to use the functionality of the Yii model for working with files. In fact, if a model is matched to each downloaded file, all low-level operations, including downloading, can be assigned to it. And in the $modMyModel->id_image
store the primary key of this model (now the essence of the name of this property is clear). Then for $modMyModel
it will be possible to determine the corresponding links and write, for example, like this: // MyModel: public function relations() { return array( 'image' => array(self::BELONGS_TO, 'DImage', 'id_image'), ); } // : $modMyModel = new MyModel; echo $modMyModel->id_image->image($htmlOptions); // img echo $modMyModel->file->downloadLink(); //
tbl_files
table with the fields id, source, name
. Define the DFile
model associated with this table. In it we define a static upload
method: class DFile extends DActiveRecord { public $uploadPath; // public function init() { $this->uploadPath = Yii::getPathOfAlias('application.data.files'); } public static function upload($objFile) { $modFile = new DFile; $modFile->name = $objFile->name; $modFile->source = uniqid(); if ($objFile->saveAs($modFile->uploadPath . '/' . $modFile->source)) if ($modFile->save()) return $modFile; return null; } }
DImage extends DFile
. We will need it later.beforeSave()
method: class MyModel extends DActiveRecord { public $id_image; public function rules(){ return array( array('id_image', 'DImageValidator', 'allowEmpty' => true), ); } public function relations() { return array( 'image' => array(self::BELONGS_TO, 'DImage', 'id_image'), ); } public function beforeSave() { foreach ($this->attributes as $key => $attribute) if ($attribute instanceof CUploadedFile) { $modFile = DFile::upload($attribute); // DFile $this->$key = $modFile->id; } return parent::beforeSave(); } }
$modMyModel
object, you can access the file via $modMyModel->image
. How profitable it is to use - read on.DFileValidator
and DImageValidator
will perfectly cope with this, in which you can specify all the necessary rules.DFile
. Like that: class DFile extends DActiveRecord { public function downloadLink($htmlOptions = array()) { return CHtml::link($this->name, array('/files/file/download', 'id' => $this->id), $htmlOptions); } }
FileController
controller. We define it: class FileController extends DcController { public function actionDownload($id) { $modFile = $this->loadModel($id); header("Content-Type: application/force-download"); header("Content-Type: application/octet-stream"); header("Content-Type: application/download"); header("Content-Disposition: attachment; filename=" . $modFile->name); header("Content-Transfer-Encoding: binary "); readfile($modFile->uploadPath . '/' . $modFile->source); } public function loadModel($id) { $modFile = DFile::model()->findByPk($id); if($modFile === null) throw new CHttpException(404,'The requested page does not exist.'); return $modFile; } }
actionDownload()
method does not make any additional checks, but you can easily enable them if necessary. In the model now, having defined the appropriate link, you can write $modMyModel->file->downloadLink()
. Of course, this approach will be less productive than issuing direct links to files. If performance is critical, you can order files not in a protected data
directory, but in another (open) directory, and give direct links.assets
folder. Secondly, the publication will not be possible to implement the standard means of Yii. The fact is that for each published file, Yii will create its own folder, which will be brute force. Yes, and the creation of thumbnails immediately in the folder of assets
using standard tools will not work.assets
folder, but create a link (the standard publishing mechanism in Yii suggests publishing with link creation).DImage
class inherited from DFile
. We describe the creation of thumbnails and publication: class DImage extends DFile { public $assetsPath; // public $assetsUrl; // URL public $thumbs = array( 'min' => array('width' => 150, 'height' => 150), 'mid' => array('width' => 250), 'big' => array('width' => 600), ); // public function init() { $this->assetsUrl = Yii::app()->assetManager->baseUrl . '/files'; $this->assetsPath = Yii::app()->assetManager->basePath . '/files'; if (!is_dir($this->assetsPath)) mkdir($this->assetsPath); } // $this->assetsPath public function getIsPublished() { foreach ($this->thumbs as $kThumb => $vThumb) if (!is_file($this->assetsPath . '/' . $this->source . '_' . $kThumb)) return false; return true; } // public function publish() { if (!$this->isPublished) foreach ($this->thumbs as $kThumb => $vThumb) $this->createThumb($this->uploadPath . '/' . $this->source, $this->assetsPath . '/' . $this->source . '_' . $kThumb, $kThumb); return $this->assetsUrl . '/' . $this->source; } // function createThumb($strSrcFile, $strDstFile, $strThumb) { // $strSrcFile, $strDstFile } }
files
in the assets
folder. Considering that it is recommended to periodically clean the assets/files
folder, it is necessary to check the existence of the assets/files
folder every time. And create if needed. The name of the thumbnail is equal to the name of the image, supplemented by the identifier of the thumbnail. The image is considered published if all the miniatures are in place. There is no sense in checking the date of the original and published files, since the uploaded file cannot be changed. The publish()
function returns the URL of the published image (but without a thumbnail), which does not contradict the idea of publishing resources in Yii.DImage
add the DImage
class DImage
the image()
method: public function image($strThumb = 'min', $htmlOptions = array()) { return CHtml::image($this->publish() . '_' . $strThumb, $this->name, $htmlOptions); }
$modMyModel->image->image()
in the model. By the way, if the size of the thumbnails suddenly needs to be changed or added a new one (it happened to me once), and all the files have been uploaded, it will be enough to change the size in the settings and clear the assets folder.DActiveForm extends CActiveForm
. Storage of settings can be assigned to the files
module.FileController
controller. But how then publish miniatures?Source: https://habr.com/ru/post/156293/
All Articles