前端:http://blog.csdn.net/qq_29238009/article/details/77649524
我这里后台用的是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