總結:ajax多檔案上傳,laravel後臺篇
我這裡後臺用的是php框架laravel寫的,如果你是用這個框架或者類似框架寫的話,可以參考下我寫的程式碼,如果是用其他語言寫的話,可以看看我寫的流程。
—————————————————————正文———————————————————————
controller:
public function uploadFile(){
/*
將檔案的key處理出來,info欄位傳來的是形如 "A,B,C,D," 的字串,
處理後得到['A','B','C','D']
*/
$names = array_diff(explode(',' ,Input::get('info')),['']);
$this->FileService->getAndSaveFile($names);
}
service:
<?php
namespace App\Services;
use App\Exceptions\OPException;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
class FileService
{
protected $name ;
protected $info;
protected $file;
function __construct(){
$this->name = [];
$this->file = [];
$this->info = [];
}
/**
* 根據name獲取並儲存file
*/
public function getAndSaveFile($name){
$this->name = $name;
$this->checkFile(); //獲取合法檔案列表
$this->info = $this->getInfo(); //獲取檔案資訊列表
if(!$this->saveFile()) //處理儲存所有檔案
throw new OPException('檔案儲存失敗,請稍後重試',10002);
else{
$infos = [];
foreach($this->info as $v){
$info = [];
$info['src'] = $v['src'];
$info['size'] = $v['size'];
$info['realName'] = $v['name'];
$info['created_at'] = Carbon::now();
$info['updated_at'] = Carbon::now();
$infos[] = $info;
}
try{
DB::table('test_attachment')->insert($infos);
}catch (\Exception $e){
throw new OPException('檔案上傳成功,但是資訊入庫失敗!',10003,$infos);
}
throw new OPException('檔案上傳成功,上傳檔案數:'.count($infos),10001,array_column($infos,'realName'));
}
}
/**
* 校驗檔案是否存入記憶體
* @throws OPException
*/
private function checkFile(){
foreach($this->name as $v){
if(!app('request')->hasFile($v))
throw new OPException('無效檔案!'.$v,10001);
$this->file[$v] = app('request')->file($v);
if(!$this->file[$v]->isValid())
throw new OPException('檔案上傳失敗',10001);
}
}
/**
* 儲存檔案,資料夾要做分類,並且資料庫記錄
*/
private function saveFile(){
foreach($this->name as $v){
$dirs = [
'base'=>'upload_attachment',
'y'=>date('Y'),
'm'=>date('m'),
'd'=>date('d'),
];
//判斷根目錄是否存在
$dir = '';
foreach($dirs as $k=>$v2){
$dir .= $v2;
if(!Storage::exists($dir))
Storage::makeDirectory($dir);
$dir .= '/';
}
$fileName = $this->generateName($dir,$v);
$this->file[$v]->move(storage_path($dir),$fileName);
if(!Storage::exists($dir.$fileName))
return false;
$this->info[$v]['src'] = $dir.$fileName;
}
return true;
}
/**
* 在指定目錄下,返回唯一的檔名
* @param $dir
* @return string
*/
private function generateName($dir,$no){
$time = date('H_i_s');
$fileName = md5($time.$this->info[$no]['name'].rand(0,9999).rand(0,9999).rand(0,9999)).'.'.$this->info[$no]['extension'];
for(;;){
if(Storage::exists($dir.$fileName))
$fileName = md5($time.$this->info[$no]['name'].rand(0,9999).rand(0,9999).rand(0,9999)).'.'.$this->info[$no]['extension'];
else
return $fileName;
}
}
/**
* 獲取檔案資訊(size,name,extension……)
*/
private function getInfo(){
$infos = [];
foreach($this->name as $v){
$infos[$v] = [
'size' => $this->file[$v]->getSize(),
'extension' => $this->file[$v]->getClientOriginalExtension(),
'name' => $this->file[$v]->getClientOriginalName()
];
}
return $infos;
}
}
突然感覺自己寫的註釋都挺完整的了(~ ̄▽ ̄)~ ,也沒有什麼難理解的地方,我就不贅述了,大概流程就是:
(1) checkFile()保證所有檔案都完整傳到了快取,然後用this->file儲存每個檔案備用
(2) getInfo(),將this->file的檔案資訊都處理出來,放到this->info備用
(3) saveFile()分類儲存所有檔案,將某個年月日上傳的檔案放到同一個資料夾裡分類儲存
(當然你可以再細分目錄,加上時分秒和上傳者使用者之類的),
然後生成唯一的檔名字(因為不同人可能在同一天上傳同一個名字的檔案,
所以我們生成隨機名字替換掉原來檔名進行儲存。每次生成隨機檔名都檢查一下在該目錄有沒有同名的隨機檔名,
如果有再生成,直到唯一存在,防止那極端的重名機率),然後將相對於儲存根目錄的地址存到this->info
(4)到這裡沒拋錯,那麼檔案就應該已經存在於伺服器指定的資料夾中了,
後面一小段我們將檔案資訊this->info存進資料庫中。
以後我們要找檔案檔案就根據表中的對應關係找到原檔案的路徑就能找到了。
這裡的資料其實至少還要有使用者欄位才能合理使用的(我這裡是測試就我一個人用的,就不寫上去了),
不然使用者就找不到原來的檔案啦
(畢竟原檔名很容易重名,好多個同名的原檔名也不知道是誰上傳的哪個)。
加上使用者欄位後,以後資料庫搜尋的時候查詢 (使用者+時間段)就能搜尋到某個使用者哪個時間段上傳的所有檔案了
返回原檔名+id到前端,然後根據使用者選擇檔案,搜尋唯一id然後對應找到那個檔案的相對路徑,再返回下載
(記得返回下載的時候要在header把原來的名字替換回去再返回response下載哦)
補充說明:
1.上面程式碼我寫了很多丟擲錯誤,並不會直接報出錯誤,而是處理後返回json給前端。
如果出錯了需要停止指令碼,直接在service裡面return json,指令碼是不會中斷的,
因為這是return 到了controller,讓controller再判斷終止指令碼就太麻煩了,就沒有靈性(。・ω・。) 。
Exception能在任意指令碼層面讓整個指令碼中斷,然後laravel的Exception有個設定是能在拋錯前做處理的
這裡的OPException就是在handle拋錯之前處理返回json,而不是直接拋錯,
這樣就既做到了靈活中斷指令碼,然後又規避了簡單得拋錯
(畢竟我這裡是要中斷指令碼然後返回一個json錯誤資訊,而不是想要一個單純的Exception)。
這招我也是從公司前輩那裡抄來的,就很靈性(≖ᴗ≖)✧,
laravel的使用者可以看看官方文件-》服務-》錯誤&日誌:
http://laravelacademy.org/post/195.html