Потоковое сжатие изображений при загрузке на сервер (Yii2 и не только)
Интро
Вчера я закончил работу над CRM системой, где заказчик попросил осуществить следующую схему: к клиенту надо было приложить сканы паспорта, но дабы не забивать место на диске, надо было сжать их при загрузке на сервер, если они превышали размер в два мегабайта. Искал информацию по этому поводу, но нашел только создание миниатюр и ресайз. Поэтому попробовал включить голову и быстро сделал аналогию, но без пересохранения исходного изображения. Всё это сделал с использованием фреймворка Yii2.
Model
Для загрузки изображений у меня была создана модель ActiveRecord, описывающая кросс-таблицу, которая хранила в себе айди кейса исходное название файла на сервере и создающееся автоматически имя файла на сервере.
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 |
<?php namespace app\modules\crm\models; use yii\behaviors\TimestampBehavior; use Yii; /** * This is the model class for table "xnvcfiles". * * @property integer $id * @property integer $nvcid * @property string $fileurl * @property integer $created_at * @property integer $updated_at */ class Xnvcfiles extends \yii\db\ActiveRecord { /** * @inheritdoc */ public $dopfiles; public static function tableName() { return 'xnvcfiles'; } public function behaviors() { return [ TimestampBehavior::className(), ]; } /** * @inheritdoc */ public function rules() { return [ [['nvcid', 'created_at', 'updated_at'], 'integer'], [['name'], 'safe'], [['dopfiles'], 'safe'], [['fileurl'], 'string', 'max' => 200], ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => Yii::t('app', 'ID'), 'nvcid' => Yii::t('app', 'Nvcid'), 'fileurl' => Yii::t('app', 'Fileurl'), 'created_at' => Yii::t('app', 'Created At'), 'updated_at' => Yii::t('app', 'Updated At'), ]; } } |
В коде используется переменная $dopfiles, которую мы потом исользуем, чтобы собрать несколько изображений.
Controller
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 |
<?php namespace app\modules\crm\controllers; use Yii; use app\modules\crm\models\Nvc; use app\modules\crm\models\Xnvcfiles; use yii\web\Controller; use yii\web\UploadedFile; use yii\data\ActiveDataProvider; use yii\imagine\Image; use Imagine\Image\Box; class NvcController extends Controller { public function actionCreate() { $model = new Nvc();//Модель кейса $xnvcfiles = new Xnvcfiles();//Модель кросс таблицы if ($model->load(Yii::$app->request->post())&&$xnvcfiles->load(Yii::$app->request->post())) { //Обрабатываем модель кейса и сохраняем. $model->save(); //Приступаем к обработке изображений. if($files = UploadedFile::getInstances($xnvcfiles, 'dopfiles')){//Здесь переменная dopfiles без квадратных скобок! foreach($files as $file){ $xnvcfiles = new Xnvcfiles();//Для каждой фотографии/файла создаем новую модель. $xnvcfiles->name = $file->name;//Сохраняем исходное имя файла (пригодится, если файлы надо будет обратно скачивать с сервера) $xnvcfiles->nvcid = $model->id;//Приписываем айди сохраненного кейса. $ext = end((explode(".", $file->name))); находим расширение $xnvcfiles->fileurl = Yii::$app->security->generateRandomString(100).".{$ext}";//Генерируем новое имя файла для сохранения на серевере, чтобы не было перезаписи. $size=$file->size; if($size>2000000&&preg_match("/(\.jpg|\.jpeg|\.png|\.gif|)/i",$exp)){//если файл весит больше 2 Мб и является расширением. $imagine = Image::getImagine(); $smimage = $imagine->open($file->tempName);//Открываем временное изображение, существующее на сервере только до следующего request. $quality = round(2000000/$size*100); //Высчитваем на сколько процентов надо снизить качество изображения, чтобы быть в пределах 2 Мб $smimage->save('uploads/files/'.$xnvcfiles->fileurl, ['quality' => $quality]);// Сохраняем с учетом выситанного ккачества. } else{//Если не изображение, то сохраняем как есть. $path = 'uploads/files/' . $xnvcfiles->fileurl; $file->saveAs($path); } $xnvcfiles->save(false); } } return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('create', [ 'model' => $model, 'xnvcfiles'=>$xnvcfiles, ]); } } } |
View
Здесь надо сказать, что я использовал расширение для Yii2 (https://github.com/kartik-v/yii2-widget-fileinput). Но можно без пролем использовать и обычный встроенный FileInput.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?php use yii\helpers\Html; use yii\widgets\ActiveForm; use kartik\file\FileInput; ?> <div class="nvc-form"> <?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]); ?> //.... <?echo $form->field($xnvcfiles, 'dopfiles[]')->widget(FileInput::classname(), [ 'options' => ['multiple' => true], 'pluginOptions' => ['previewFileType' => 'any'] ])->label("Дополнительные документы");?> //.... <div class="form-group"> <?= Html::submitButton($model->isNewRecord ? Yii::t('app', 'Create') : Yii::t('app', 'Update'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?> </div> <?php ActiveForm::end(); ?> </div> |
В итоге главной ступенью потокового сжатия данных является контроллер, где мы просто рассчитываем понижение качества и сохраняем с его учетом.