This is an old revision of the document!
Yii 2 File Upload
Example CSV File Upload
Model
Create DataUploadForm
model:
<?php namespace app\models; use yii\base\Model; use yii\web\UploadedFile; class DataUploadForm extends Model { const OVERWRITE_NO = 0; const OVERWRITE_YES = 1; const IMPORT_METHOD_UPDATE_INSERT = 0; // Update existing data (update, or insert if not exist) const IMPORT_METHOD_UPDATE = 1; // Update existing data (update only, no insert if not exist) const IMPORT_METHOD_APPEND_ALL = 2; // Append all to existing data (insert only, but all) const IMPORT_METHOD_APPEND_NEW = 3; // Append only new to existing data (insert only, but only new) const IMPORT_METHOD_REPLACE = 4; // Replace existing data (delete all, then insert) public static $importMethods = [ DataUploadForm::IMPORT_METHOD_UPDATE_INSERT => 'Update existing / Insert new content', DataUploadForm::IMPORT_METHOD_UPDATE => 'Update only existing content', DataUploadForm::IMPORT_METHOD_APPEND_ALL => 'Append all content', DataUploadForm::IMPORT_METHOD_APPEND_NEW => 'Append only new content', DataUploadForm::IMPORT_METHOD_REPLACE => 'Replace content', ]; /** * @var UploadedFile */ public $dataFile; public $overwriteExistingContent = self::OVERWRITE_NO; public $importMethod = self::IMPORT_METHOD_UPDATE_INSERT; public function rules() { return [ // see '@vendor/yiisoft/yii2/validators/FileValidator.php' for all options [['dataFile'], 'file', 'skipOnEmpty' => false, 'maxFiles' => 1, // Extensions: For CSV files, use uppercase CSV extension in actual file, otherwise it fails. 'extensions' => 'txt, CSV, xls', // MimeTypes: must match file type (extension) to be uploaded. Eg: txt, csv, png, csv (in excel). //'mimeTypes' => "text/plain, text/CSV, image/png, application/vnd.ms-excel", //'mimeTypes' => "*", // any MIME type 'checkExtensionByMimeType' => false, // false to stop verifying file extension to MIME types 'wrongExtension' => '{attribute} = [{file}]. Only these extensions are allowed: {extensions}.' ], ]; } public function upload() { if ($this->validate()) { $this->dataFile->saveAs('uploads/' . strtolower($this->dataFile->baseName) . '.' . strtolower($this->dataFile->extension) ); // save to [app]/web/uploads return true; } else { return false; } } //public function attributeLabels() //{ // return ['file' => Yii:t('app', 'Archivo')]; // es translation //} } ?>
Controller
Add these actions to your controller. Eg: [app]/controllers/ItemController
:
namespace app\controllers; use Yii; use app\models\Item; use app\models\ItemSearch; use app\models\DataUploadForm; use yii\web\Response; use yii\web\Request; use yii\web\UploadedFile; use yii\filters\AccessControl; use yii\filters\VerbFilter; //... defined('DS') or define('DS', DIRECTORY_SEPARATOR); // set const if not defined class ItemController extends Controller { public function behaviors() { return [ 'access' => [ 'class' => AccessControl::className(), 'rules' => [ [ 'actions' => [ 'index', 'create', 'view', 'update', 'delete', 'report', 'download-sample', 'batch-create', ], 'allow' => true, 'roles' => ['@'], // @ = Authenticated users ], //[ // 'allow' => true, // 'actions' => ['index', 'view'], // 'roles' => ['?'], // ? = Guest user //], ], ], //... ]; } //... /** * Creates a downloadable sample CSV file to be used in batch-create. * It return processing messages. * @return mixed */ public function actionDownloadSample($file_name = 'file.csv') { $data = [ ["code", "description", "quantity"], ["AHI-POUCH", "Audina Navy Blue Pouch", "800"], ["NOLOGO-SDELIVERYCASE", "Audina S-Delivery White Case No Logo", "96"], ]; $this->actionOutputCSV($data, $file_name); } // Usage: // actionOutputCSV(array( // array("Volvo", "BMW", "Toyota"), // array("Volvo 1", "BMW 1", "Toyota 1"), // array("Volvo 2", "BMW 2", "Toyota 2"), // array("Volvo 3", "BMW 3", "Toyota 3"), // ),'download.csv'); // Source: See more at: https://arjunphp.com/create-download-csv-files-php/#sthash.f6JCTy4J.dpuf function actionOutputCSV($data, $fileName = 'file.csv', $is_csv_excel=false) { # output headers so that the file is downloaded rather than displayed header("Content-Type: text/csv"); header("Content-Disposition: attachment; filename=$fileName"); # Disable caching - HTTP 1.1 header("Cache-Control: no-cache, no-store, must-revalidate"); # Disable caching - HTTP 1.0 header("Pragma: no-cache"); # Disable caching - Proxies header("Expires: 0"); # Start the ouput $output = fopen("php://output", "w"); if ($is_csv_excel) { fwrite($output, "sep=\t"); // set default separator to TAB, so Excel can import CSV as tab-separated } # Then loop through the rows foreach ($data as $row) { # Add the rows to the body //fputcsv($output, $row); // here you can change delimiter/enclosure fputcsv($output, $row, "\t"); // here you can change delimiter/enclosure } # Close the stream off fclose($output); } /** * Creates Item models in batch. * If create is successful, the browser will be redirected to the 'index' page. * @return mixed */ public function actionBatchCreate() { $model = new DataUploadForm(); if (Yii::$app->request->isPost) { $model->dataFile = UploadedFile::getInstance($model, 'dataFile'); $model->overwriteExistingContent = ( isset($_POST['DataUploadForm']['overwriteExistingContent']) ? $_POST['DataUploadForm']['overwriteExistingContent'] : DataUploadForm::OVERWRITE_NO ); $model->importMethod = ( isset($_POST['DataUploadForm']['importMethod']) ? $_POST['DataUploadForm']['importMethod'] : DataUploadForm::IMPORT_METHOD_APPEND ); if ($model->upload()) { // file is uploaded successfully Yii::$app->session->setFlash('success', "File {$model->dataFile} was uploaded successfully."); // clear table if ($model->overwriteExistingContent) { Item::deleteAll(); } // import data $resultsImport = $this->importDataFromCSV($model->dataFile, $model->importMethod); if ($resultsImport) { $panelCollapsed = ' <button class="btn btn-primary" type="button" '; $panelCollapsed .= ' data-toggle="collapse" '; $panelCollapsed .= ' data-target="#collapseExample" '; $panelCollapsed .= ' aria-expanded="false" '; $panelCollapsed .= ' aria-controls="collapseExample"> View Import Results </button>'; $panelCollapsed .= '<div class="collapse" id="collapseExample">'; $panelCollapsed .= ' <div class="well">' . $resultsImport . '</div>'; $panelCollapsed .= '</div>'; Yii::$app->session->setFlash('success', "Item data was imported successfully. " . $panelCollapsed ); } else { Yii::$app->session->setFlash('error', "Item data failed to be imported. "); } return $this->redirect(['index']); } else { Yii::$app->session->setFlash('error', "File {$model->dataFile} failed to be uploaded."); } } return $this->render('batch-create', ['model' => $model]); } /** * Updates an existing Item model. * If update is successful, the browser will be redirected to the 'index' page. * @return mixed */ public function actionBatchUpdate() { // // Retrieve items to be updated in a batch mode, // // assuming each item is of model class 'Item' // $items=$this->getItemItemsToUpdate(); // if (Model::loadMultiple($items, Yii::$app->request->post()) && Model::validateMultiple($items)) { // $count = 0; // foreach ($items as $item) { // // populate and save records for each model // if ($item->save()) { // // do something here after saving // $count++; // } // } // Yii::$app->session->setFlash('success', "Processed {$count} records successfully."); // return $this->redirect(['index']); // redirect to your next desired page // } else { // return $this->render('update', [ // 'order-items' => $items, // ]); // } } /** * Imports a CSV file to be used in batch-create. * It return processing messages. * @return mixed */ private function importDataFromCSV($fileName, $importMethod=DataUploadForm::IMPORT_METHOD_UPDATE_INSERT) { $output = "<pre>\n"; $output .= "Processing data\n"; // Preprocess based on Import Method switch($importMethod) { case DataUploadForm::IMPORT_METHOD_APPEND_ALL: // Append content (insert all) case DataUploadForm::IMPORT_METHOD_APPEND_NEW: // Append only new to existing data (insert only, but only new) case DataUploadForm::IMPORT_METHOD_UPDATE_INSERT: // Update content (update, or insert if new) case DataUploadForm::IMPORT_METHOD_UPDATE: // Update content (update only, no insert if new) default: break; case DataUploadForm::IMPORT_METHOD_REPLACE: // Replace existing data (delete all, then insert) Item::deleteAll(); break; } // open the source data file //$dataFilename = Yii::$app->basePath . DS . 'commands' . DS . 'data' . DS . 'Item-data.csv'; $dataFilename = Yii::$app->basePath . DS . 'web' . DS . 'uploads' . DS . strtolower($fileName); if (!file_exists($dataFilename)) { //echo "Missing data file: [" . $dataFilename . "]\n"; Yii::$app->session->setFlash('error', "Missing data file: [{$dataFilename}]"); return false; //return "Missing data file: [" . $dataFilename . "]\n"; } $rowCnt = 0; $dataFile = fopen($dataFilename, "r") or die("Unable to open file!"); while(!feof($dataFile)) { // grab one line from file $data = fgets($dataFile); // parse data $row_fields = explode("\t", $data); // Skip header if ((trim($row_fields[0]) == '') || (trim($row_fields[0]) == 'sep=') || (trim($row_fields[0]) == 'code') || (trim($row_fields[0]) == '------') ) { $output .= "### Skipped: {$data}. \n"; continue; // skip this header row } // Get row data if (count($row_fields)>0) { $code = trim(trim($row_fields[0]), '"'); $description = trim(trim($row_fields[1]), '"'); $quantity = trim(trim($row_fields[2]), '"'); } else { break; // stop processing rows } switch($importMethod) { case DataUploadForm::IMPORT_METHOD_APPEND_ALL: // Append content (insert all) case DataUploadForm::IMPORT_METHOD_UPDATE_INSERT: // Update content (update, or insert if new) default: if (($model = Item::findOne(['code'=>$code])) === null) { $model = new Item(); // if existing row not found, we add as new record. } break; case DataUploadForm::IMPORT_METHOD_APPEND_NEW: // Append only new to existing data (insert only, but only new) if (($model = Item::findOne(['code'=>$code])) !== null) { $processThisRow = false; // Skip this existing row. We only want to insert new records. } else { $model = new Item(); } break; case DataUploadForm::IMPORT_METHOD_UPDATE: // Update content (update only, no insert if new) if (($model = Item::findOne(['code'=>$code])) === null) { $processThisRow = false; // Skip this new row. We only want to update existing records. } break; case DataUploadForm::IMPORT_METHOD_REPLACE: // Replace existing data (delete all, then insert) $model = new Item(); // we only add as new record (any previous records were deleted at beginning of this function) break; } // insert data if ($processThisRow) { $model->code = $code; $model->description = $description; $model->quantity = $quantity; } $rowCnt++; // for printing, it is always one ahead if ($model->save()) { $entry_id = $model->id; $output .= "{$rowCnt}. ### Inserted Item [{$entry_id}] ###\n"; $row_fieldsData = "[".implode(',', [ $code, $description, $quantity ])."]"; $output .= "{$rowCnt}. Insert Item {$row_fieldsData}\n"; } else { $row_fieldsData = "[".implode(',', [ $code, $description, $quantity ])."]"; $output .= "{$rowCnt}. Failed to insert Item {$row_fieldsData}. " . print_r($model->getErrors(), true) . "\n"; } } $output .= "</pre>\n"; fclose($dataFile); return $output; } }
View
Add a button to do a Batch Upload. Update view index
under views. Eg: [app]/views/items/index
<?php //... <?= Html::a(Yii::t('app', 'Batch Upload'), ['batch-create'], ['class' => 'btn btn-success']) ?> //... ?>
Create view batch-create
under views. Eg: [app]/views/items/batch-create
:
<?php use yii\helpers\Html; use yii\helpers\Url; use yii\widgets\ActiveForm; use yii\helpers\ArrayHelper; $this->title = Yii::t('app', 'Batch Upload'); $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Item'), 'url' => ['index']]; //$this->params['breadcrumbs'][] = $this->title; $this->params['breadcrumbs'][] = Yii::t('app', 'Batch Upload'); ?> <?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?> <h2>Item (Batch Upload)</h2> <div class="col-lg-12"> <?php if(Yii::$app->session->hasFlash('success')): ?> <div class="alert alert-success" role="alert"> <?= Yii::$app->session->getFlash('success'); ?> </div> <?php endif; ?> <?php if(Yii::$app->session->hasFlash('error')): ?> <div class="alert alert-danger" role="alert"> <?= Yii::$app->session->getFlash('error'); ?> </div> <?php endif; ?> </div> <div class="alert alert-info" role="alert"> <h4>Data File</h4> Create a CSV (<kbd>tab</kbd>-delimited) file with the following format. Eg: <table class="table table-bordered table-striped table-hover table-condensed" border="1"> <tr> <td>code</td><td>description</td><td>quantity</td> </tr> <tr class="warning"> <td>ACME-POUCH</td><td>Branded Pouch</td><td>800</td> </tr> <tr> <td>NOLOGO-POUCH</td><td>Unbranded Pouch</td><td>96</td> </tr> </table> <?= Html::a('<i class="glyphicon glyphicon-download d-icon"></i>' . ' Download Sample ', ['item/download-sample', 'file_name'=>'sample.csv'], ['class'=>'btn btn-default']) ?> </div> <?= $form->field($model, 'dataFile')->fileInput(['class'=>'form-control']) ?> <?= $form->field($model, 'overwriteExistingContent')->dropDownList( ['0'=>'No', '1'=>'Yes'] ) ?> <button class='btn btn-success'>Submit</button> <?php ActiveForm::end() ?>
Web
Create an uploads
directory in web folders. Eg: [app]/web/uploads
Example Image Upload and Save Filename in DB
Model
In the model, override afterSave()
to store image file to server, and its path in the database. Eg: @app/models/Organization.php
:
public function afterSave($insert, $changedAttributes) { if(isset($this->logo)) { $this->logo = UploadedFile::getInstance($this, 'logo'); if(is_object($this->logo)) { $path = Yii::$app->basePath . '/images/'; // set directory path to save image $this->logo->saveAs("{$path}{$this->id}_{$this->logo}"); // saving image in folder $this->logo = "{$this->id}_{$this->logo}"; // appending id to image name \Yii::$app->db->createCommand() ->update('organization', ['logo' => $this->logo], 'id = "'.$this->id.'"') ->execute(); // manually update image name to db } } }
Controller
Add action actionCreate()
to controller. Eg: @app/controllers/OrganizationController.php
:
public function actionCreate() { $model = new Organization(); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('create', [ 'model' => $model, ]); } }
Source: StackOverflow: Yii 2 Upload and Save Image Files Locally and Image URL is Saved in DB
Example Image File Upload
Model
File @app/models/ImageFileUpload.php
:
<?php namespace app\models; use yii; use yii\base\Model; use yii\web\UploadedFile; defined('DS') or define('DS', DIRECTORY_SEPARATOR); class ImageUploadForm extends Model { /** * @var UploadedFile */ public $imageFile; public function rules() { return [ // see '@vendor/yiisoft/yii2/validators/FileValidator.php' for all options [['imageFile'], 'file', 'skipOnEmpty' => false, 'maxFiles' => 1, 'extensions' => 'png, jpg', //'mimeTypes' => "image/png, image/jpg", // must match the file type (extension) to be uploaded. Eg: png, jpg //'mimeTypes' => "*", // any MIME type 'checkExtensionByMimeType' => false, // false to stop verifying file extension to MIME types 'wrongExtension' => '{attribute} = [{file}]. Only files with these extensions are allowed: {extensions}.' ], ]; } public function upload() { if ($this->validate()) { //$this->imageFile->saveAs( // strtolower("uploads/{$this->imageFile->baseName}.{$this->imageFile->extension}") //); // save to [app]/web/uploads $this->imageFile->saveAs("uploads/" . $this->getFileName()); // save to [app]/web/uploads return true; } else { return false; } } public function getFileName() { return strtolower("{$this->imageFile->baseName}.{$this->imageFile->extension}"); } public function getFilePath() { return Yii::$app->basePath . DS . 'web' . DS . 'uploads' . DS . $this->getFileName(); } public function getFileUrl() { return Yii::$app->homeUrl . "/uploads/" . $this->getFileName(); } } ?>
File @app/models/Tool.php
:
<?php namespace app\models; use Yii; use yii\base\Model; // parent for model classes not associated with database tables //use yii\db\ActiveRecord; // parent for model classes that do correspond to database tables define('PNG_DPI', 75); define('LABEL_DPI', 300); define('LABEL_WIDTH', 6); define('LABEL_HEIGHT', 4); define('LABEL_MARGIN', 5); define('PNG_FONT', 5); /** * This is the model class for table "Tools". * * @property array $countries * @property array $countryNames * @property array $languages * @property array $states */ class Tool extends Model { //... /** * @inheritdoc * Usage: * return '<img src="data:image/png;base64,' . base64_encode(\app\models\Tool::generateEmptyPng()) . '"/>'; */ public static function generateEmptyPng() { $image = imagecreate(PNG_DPI * LABEL_WIDTH, PNG_DPI * LABEL_HEIGHT); $canvas = imagecreate(PNG_DPI * LABEL_WIDTH, PNG_DPI * LABEL_HEIGHT); // Setup color //$color_background = imagecolorallocate($image, 255, 255, 255); // Background = white $color_background = imagecolorallocate($image, 200, 200, 200); // Background = gray $color_text = imagecolorallocate($image, 0, 0, 0); // Foreground = black // Background imagefill($image, 0, 0, $color_background); // Image border //imagerectangle($image, // LABEL_MARGIN, LABEL_MARGIN, // (LABEL_WIDTH*PNG_DPI)-LABEL_MARGIN, // (LABEL_HEIGHT*PNG_DPI)-LABEL_MARGIN, // $color_text //); // Text content imagestring($image, PNG_FONT, 20, 20, 'NO IMAGE FILE', $color_text); // Copy image to target canvas imagecopy( $canvas, // dst $image, // src 0, 0, // dst x,y 0, 0, // src x,y (LABEL_WIDTH * PNG_DPI), // src width (LABEL_HEIGHT * PNG_DPI) // src height ); imagedestroy($image); // Capture the full image from canvas ob_start(); imagepng($canvas); $imageData = ob_get_contents(); ob_end_clean(); return $imageData; } } ?>
Controller
File @app/controllers/ItemController.php
:
//... use app\models\ImageUploadForm; defined('DS') or define('DS', DIRECTORY_SEPARATOR); class ItemController extends Controller { //... public function actionUploadImage($id) { $model = new ImageUploadForm(); $item = $this->findModel($id); if (Yii::$app->request->isPost) { $model->imageFile = \yii\web\UploadedFile::getInstance($model, 'imageFile'); if ($model->upload()) { // file is uploaded successfully Yii::$app->session->setFlash('success', "File <tt>{$model->imageFile}</tt> was uploaded successfully." ); // Assign file to item // (rename to item_code if available, otherwise keep original filename) if (!empty($item->code)) { $targetFile = "{$item->code}.{$model->imageFile->extension}"; } else { $targetFile = $model->getFileName(); } $baseWebPath = Yii::$app->basePath . DS . 'web'; //$sourcePath = $baseWebPath . DS . $model->getFileName(); $sourcePath = $model->getFilePath(); $targetDir = $baseWebPath . DS . 'img' . DS . 'data' . DS . Yii::$app->controller->id; $targetPath = $targetDir . DS . $targetFile; if (!file_exists($targetDir)) { mkdir($targetDir, 0755, true /* recursive */); } $resultsImport = move_uploaded_file($sourcePath, $targetPath); //$resultsImport = $model->imageFile->saveAs($targetPath); // save to [app]/web/img/data/item // When move_uploaded_file() fails to work, we go old school and force it to do it. if (!$resultsImport) { $resultsImport = copy($sourcePath, $targetPath); // copy source file to target unlink($sourcePath); // delete source file } $item->image_file = $targetFile; $item->save(); // import data if ($resultsImport) { Yii::$app->session->setFlash('success', "Item image <tt>{$model->imageFile}</tt> was uploaded successfully." ); } else { $msg = "Item image <tt>{$model->imageFile}</tt> failed to be moved.<br>"; $msg .= "- Source:<tt>{$sourcePath}</tt> <br>"; $msg .= "- Target:<tt>{$targetPath}</tt>"; Yii::$app->session->setFlash('error', $msg); } return $this->redirect(['view', 'id' => $id]); } else { Yii::$app->session->setFlash('error', "File {$model->imageFile} failed to be uploaded."); } } return $this->render('upload-image', [ 'model' => $model, 'item' => $item, ]); } }
View
File @app/views/item/upload-image
:
<?php use yii\helpers\Html; use yii\helpers\Url; use yii\widgets\ActiveForm; use yii\helpers\ArrayHelper; $this->title = Yii::t('app', 'Image Upload'); $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Item'), 'url' => ['index']]; $this->params['breadcrumbs'][] = ['label' => "{$item->code} {$item->description}", 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; ?> <?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?> <h2><?= $this->title ?></h2> <div class="col-lg-12"> <?php if(Yii::$app->session->hasFlash('success')): ?> <div class="alert alert-success" role="alert"> <?= Yii::$app->session->getFlash('success'); ?> </div> <?php endif; ?> <?php if(Yii::$app->session->hasFlash('error')): ?> <div class="alert alert-danger" role="alert"> <?= Yii::$app->session->getFlash('error'); ?> </div> <?php endif; ?> </div> <?= $form->field($model, 'imageFile')->fileInput(['class'=>'form-control']) ?> <button class='btn btn-success'>Submit</button> <?php ActiveForm::end() ?>
File @app/views/item/view.php
to call @app/views/item/upload-image.php
action:
<?php $itemImage = (!empty($model->image_file) ? Yii::$app->homeUrl . 'img/data/' . Yii::$app->controller->id . "/{$model->image_file}" : '' ); ?> <!-- Image Thumbnail --> <?php if(!empty($itemImage)): ?> <img class="img-thumbnail" src="<?= $itemImage ?>" alt="Item Image"> <?php else: ?> <img class="img-thumbnail" src="data:image/png;base64,<?= base64_encode(\app\models\Tool::generateEmptyPng()) ?>"/> <?php endif; ?> <!-- Button trigger modal for Image --> <?= Html::button('<i class="fa fa-image" aria-hidden="true"></i> ' . Yii::t('app', 'View Image'), [ 'class' => 'btn btn-default', 'data-toggle' => "modal", 'data-target' => "#modalImagePreview" ]) ?> <?= Html::a('<i class="fa fa-plus" aria-hidden="true"></i> ' . Yii::t('app', 'Add Image'), ['upload-image', 'id' => $model->id], ['class' => 'btn btn-default']) ?> <!-- Modal for Image --> <div class="modal fade" id="modalImagePreview" tabindex="-1" role="dialog"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">Item Image</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <?php if(!empty($itemImage)): ?> <img class="img-fluid" src="<?= $itemImage ?>" alt="Item Image"> <?php else: ?> <img class="img-fluid" src="data:image/png;base64,<?= base64_encode(\app\models\Tool::generateEmptyPng()) ?>"/> <?php endif; ?> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> </div> </div> </div> </div>