要實現多張照片上傳功能, 一樣要在model、conrtoller、view三管齊下才能完成,
我會逐一說明 (以M->V->C的順序).
本次實作的目標:
一次將多張照片上傳至指定資料夾, 並將照片的檔名存到資料庫 (用,分隔每個檔名), 以便之後取用.
Model
在你的models裡的rules()新增以下code:
array('dbFieldsOfTheImage', 'file', 'types'=>'jpeg, jpg, png', 'allowEmpty'=>true),
1. 請將這邊的dbFieldsOfTheImage改為你照片上傳的資料表欄位名稱
2. types指的是, 指定的上傳檔案類型 (如果你要讓人上傳的是excel, 那就打上xls, xlsx...etc)
PS:還有許多參數可做設定, 例如檔案大小限制、錯誤訊息, 可參考 (CFileValidator)
重要:
網路上很多範例都會少了allowEmpty這個參數, 就像這篇 Multiple files uploader with CMultiFileUpload.
結果就會非常慘, 就是會發生檔案上傳了, 但是填寫表格的資料一直存不進資料庫. 然後會出現錯誤:XXX cannot be blank, 然後你就會匪夷所思, 明明檔案都上傳了, 檔名也都抓到了, 為什麼就是存不進去.
原來因為我們在model裡設定它是file, 所以它上傳的是檔案, 而不是一個字串 (檔名), 所以我們必須要設allowEmpty這個參數, 告訴model我們允許它是空的, 這樣在save的時候才不會被model的規則給擋住!!!
這部分我卡了好幾天, 真的很重要阿~ 各位.
View
一樣是改_form.php這個檔案 (框架預設), 修改以下的code:
<?php $form=$this->beginWidget('CActiveForm', array(
'id'=>'bnb-form',
'enableClientValidation'=>true,
'htmlOptions' => array('enctype' => 'multipart/form-data'),
)); ?>
<?php
$this->widget('CMultiFileUpload', array(
'model' => $model,
'attribute' => 'bnbImg',
'accept' => 'jpeg|jpg|png',
'duplicate' => '你選過這個照片了喔!',
'denied' => '請選擇jpeg、jpg、png格式的影像!',
'htmlOptions' => array('multiple' => 'multiple'),
'max' => 5,
));
?>
上傳檔案最重要的就是htmlOptions這個參數裡面的設定一定要是'enctype' => 'multipart/form-data'.
至於'enableClientValidation'=>true, 這個非常好用! 它會依你的model的規則設定來生成對應的JavaScript來做Client端的驗證, 超屌. 不過跟上傳檔案沒關係.
1.
attribute會生成HTML input的id跟name. 例如: 我資料表叫Test, 欄位是fuck, 這時候我設定'attribute' => 'fuck', 就會產生以下code:
<input id="Test_fuck" type="file" value="" name="Test[fuck][]" />
2.
accept - 允許的檔案格式
duplicate - 當選到重複的照片時跳出的警告視窗
denied - 當夾帶不是我們規定的檔案格式時, 跳出的警告視窗
'htmlOptions' => array('multiple' => 'multiple') - 可讓你一次選多張
max - 最多可上傳的檔案數量
別懷疑, 做了這些設定, yii就幫你把它生成JavaScript, 讓你爽的死去活來的!!
重要:
有些人會在CActiveForm裡做AJAX, 所以會加上這個 'enableAjaxValidation'=>false,
在表格有檔案上傳的欄位時千萬不要加那個! 因為它會讓你出錯, 以下擷取官方部分內容:
基於AJAX的驗證有一些限制:首先,它不能用於文件上傳字段;其次,它不能用於可能產生服務器端狀態改變的驗證;第三,它目前還不能用於表格式輸入的驗證。
Conrtoller
public function actionCreate()
{
$model = new Bnb;
if(isset($_POST['Bnb']))
{
$model->attributes=$_POST['Bnb'];
/*
$uploadPath Set info:
Linux: /images/bnb/ , Win: \images\bnb\
*/
$uploadPath = DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'bnb' . DIRECTORY_SEPARATOR;
if($allFilesName=$this->uploadMultiFile($model, 'bnbImg', $uploadPath))
$model->bnbImg = implode(",", $allFilesName);
if($model->save())
$this->redirect(array('view', 'id'=>$model->bnbId));
}
$this->render('create',array(
'model'=>$model,
));
}
private function uploadMultiFile($model, $attr, $path)
{
if($fileInfo = CUploadedFile::getInstances($model, $attr))
{
foreach ($fileInfo as $i=>$file)
{
$formatName = $file->name;
$file->saveAs(Yii::getPathOfAlias('webroot') . DIRECTORY_SEPARATOR . $path . $formatName); //webroot: you'r project/protected
$allFilesName[$i] = $formatName;
}
return ($allFilesName);
}
}
1. 用 DIRECTORY_SEPARATOR 是因為路徑在Linux跟windows不一樣的, 所以為了都能正常運做, 就用了比較長的寫法. (參考 DIRECTORY_SEPARATOR的作用)
2. 將上傳的code單獨寫成uploadMultiFile是為了讓整個可讀性更高, 避免程式絮亂, 以方便維護.
Finish~ thx GOD
參考資料:CMultiFileUpload