?
我們平時(shí)經(jīng)常做的是上傳文件,上傳文件夾與上傳文件類(lèi)似,但也有一些不同之處,這次做了上傳文件夾就記錄下以備后用。
首先我們需要了解的是上傳文件三要素:
1.表單提交方式:post (get方式提交有大小限制,post沒(méi)有)
2.表單的enctype屬性:必須設(shè)置為multipart/form-data.
3.表單必須有文件上傳項(xiàng):file,且文件項(xiàng)需要給定name值
上傳文件夾需要增加一個(gè)屬性webkitdirectory,像這樣:
<input id="fileFolder" name="fileFolder" type="file"??webkitdirectory>
不過(guò)webkitdirectory屬性有個(gè)問(wèn)題,只能支持高版本的chrome,不能支持低版本的IE,如ie6,ie7,ie8,不能做到全瀏覽器適配,運(yùn)行環(huán)境比較單一。
js中可以判斷文件夾中文件數(shù)量及文件夾大小是否符合要求,不符合要求不能向后臺(tái)提交:
前臺(tái)HTML模板
this.GetHtmlFiles =?function()
{
?????var?acx =?"";
?????acx +=?'<div class="file-item" id="tmpFile" name="fileItem">
????????????????<div class="img-box"><img name="file" src="js/file.png"/></div>
???????????????????<div class="area-l">
???????????????????????<div class="file-head">
????????????????????????????<div name="fileName" class="name">HttpUploader程序開(kāi)發(fā).pdf</div>
????????????????????????????<div name="percent" class="percent">(35%)</div>
????????????????????????????<div name="fileSize" class="size" child="1">1000.23MB</div>
????????????????????</div>
???????????????????????<div class="process-border"><div name="process" class="process"></div></div>
???????????????????????<div name="msg" class="msg top-space">15.3MB 20KB/S 10:02:00</div>
???????????????????</div>
???????????????????<div class="area-r">
????????????????????<span class="btn-box" name="cancel" title="取消"><img name="stop" src="js/stop.png"/><div>取消</div></span>
????????????????????<span class="btn-box hide" name="post" title="繼續(xù)"><img name="post" src="js/post.png"/><div>繼續(xù)</div></span>
???????????????????????<span class="btn-box hide" name="stop" title="停止"><img name="stop" src="js/stop.png"/><div>停止</div></span>
???????????????????????<span class="btn-box hide" name="del" title="刪除"><img name="del" src="js/del.png"/><div>刪除</div></span>
???????????????????</div>';
?????acx +=?'</div>';
?????//文件夾模板
?????acx +=?'<div class="file-item" name="folderItem">
???????????????????<div class="img-box"><img name="folder" src="js/folder.png"/></div>
???????????????????<div class="area-l">
???????????????????????<div class="file-head">
????????????????????????????<div name="fileName" class="name">HttpUploader程序開(kāi)發(fā).pdf</div>
????????????????????????????<div name="percent" class="percent">(35%)</div>
????????????????????????????<div name="fileSize" class="size" child="1">1000.23MB</div>
????????????????????</div>
???????????????????????<div class="process-border top-space"><div name="process" class="process"></div></div>
???????????????????????<div name="msg" class="msg top-space">15.3MB 20KB/S 10:02:00</div>
???????????????????</div>
???????????????????<div class="area-r">
????????????????????<span class="btn-box" name="cancel" title="取消"><img name="stop" src="js/stop.png"/><div>取消</div></span>
????????????????????<span class="btn-box hide" name="post" title="繼續(xù)"><img name="post" src="js/post.png"/><div>繼續(xù)</div></span>
???????????????????????<span class="btn-box hide" name="stop" title="停止"><img name="stop" src="js/stop.png"/><div>停止</div></span>
???????????????????????<span class="btn-box hide" name="del" title="刪除"><img name="del" src="js/del.png"/><div>刪除</div></span>
???????????????????</div>';
?????acx +=?'</div>';
?????//上傳列表
?????acx +=?'<div class="files-panel" name="post_panel">
???????????????????<div name="post_head" class="toolbar">
???????????????????????<span class="btn" name="btnAddFiles">選擇多個(gè)文件</span>
???????????????????????<span class="btn" name="btnAddFolder">選擇文件夾</span>
???????????????????????<span class="btn" name="btnPasteFile">粘貼文件和目錄</span>
???????????????????????<span class="btn" name="btnSetup">安裝控件</span>
???????????????????</div>
???????????????????<div class="content" name="post_content">
???????????????????????<div name="post_body" class="file-post-view"></div>
???????????????????</div>
???????????????????<div class="footer" name="post_footer">
???????????????????????<span class="btn-footer" name="btnClear">清除已完成文件</span>
???????????????????</div>
??????????????</div>';
?????return?acx;
};
選擇文件,選擇文件夾,粘貼文件和文件夾的邏輯
this.open_files =?function?(json)
{
?????for?(var?i = 0, l = json.files.length; i < l; ++i)
????{
?????????this.addFileLoc(json.files[i]);
?????}
?????setTimeout(function?() { _this.PostFirst(); },500);
};
this.open_folders =?function?(json)
{
????for?(var?i = 0, l = json.folders.length; i < l; ++i) {
????????this.addFolderLoc(json.folders[i]);
????}
?????setTimeout(function?() { _this.PostFirst(); }, 500);
};
this.paste_files =?function?(json)
{
?????for?(var?i = 0, l = json.files.length; i < l; ++i)
?????{
?????????this.addFileLoc(json.files[i]);
?????}
};
后臺(tái)在接收文件夾時(shí)不同之處在需要用MultipartHttpServletRequest
boolean?isMultipart = ServletFileUpload.isMultipartContent(request);
FileItemFactory factory =?new?DiskFileItemFactory();??
ServletFileUpload upload =?new?ServletFileUpload(factory);
List files =?null;
try
{
?????files = upload.parseRequest(request);
}
catch?(FileUploadException e)
{//?解析文件數(shù)據(jù)錯(cuò)誤?
????out.println("read file data error:"?+ e.toString());
????return;
??
}
?
FileItem rangeFile =?null;
//?得到所有上傳的文件
Iterator fileItr = files.iterator();
//?循環(huán)處理所有文件
while?(fileItr.hasNext())
{
?????//?得到當(dāng)前文件
?????rangeFile = (FileItem) fileItr.next();
?????if(StringUtils.equals( rangeFile.getFieldName(),"pathSvr"))
?????{
?????????pathSvr = rangeFile.getString();
?????????pathSvr = PathTool.url_decode(pathSvr);
?????}
}
?
server端的包和類(lèi)
?
文件塊處頁(yè)面,驗(yàn)證代碼部分
boolean?verify =?false;
String msg =?"";
String md5Svr =?"";
long?blockSizeSvr = rangeFile.getSize();
if(!StringUtils.isBlank(blockMd5))
{
?????md5Svr = Md5Tool.fileToMD5(rangeFile.getInputStream());
}
?
verify = Integer.parseInt(blockSize) == blockSizeSvr;
if(!verify)
{
?????msg =?"block size error sizeSvr:"?+ blockSizeSvr +?"sizeLoc:"?+ blockSize;
}
?
if(verify && !StringUtils.isBlank(blockMd5))
{
?????verify = md5Svr.equals(blockMd5);
?????if(!verify) msg =?"block md5 error";
}
?
if(verify)
{
?????//保存文件塊數(shù)據(jù)
?????FileBlockWriter res =?new?FileBlockWriter();
?????//僅第一塊創(chuàng)建
?????if( Integer.parseInt(blockIndex)==1) res.CreateFile(pathSvr,Long.parseLong(lenLoc));
?????res.write( Long.parseLong(blockOffset),pathSvr,rangeFile);
?????up6_biz_event.file_post_block(id,Integer.parseInt(blockIndex));
????
?????JSONObject o =?new?JSONObject();
?????o.put("msg",?"ok");
?????o.put("md5", md5Svr);?
?????o.put("offset", blockOffset);//基于文件的塊偏移位置
?????msg = o.toString();
}
rangeFile.delete();
out.write(msg);
?
生成文件名稱(chēng)的邏輯
public?String?genFile(int?uid,?String?md5,String?nameLoc)?throws?IOException
{
?????SimpleDateFormat?fmtDD =?new?SimpleDateFormat("dd");
?????SimpleDateFormat?fmtMM =?new?SimpleDateFormat("MM");
?????SimpleDateFormat?fmtYY =?new?SimpleDateFormat("yyyy");
????
?????Date?date =?new?Date();
?????String?strDD = fmtDD.format(date);
?????String?strMM = fmtMM.format(date);
?????String?strYY = fmtYY.format(date);
????
?????String?path =?this.getRoot() +?"/";
?????path = path.concat(strYY);
?????path = path.concat("/");
?????path = path.concat(strMM);
?????path = path.concat("/");
?????path = path.concat(strDD);
?????path = path.concat("/");
?????path = path.concat(md5);
?????path = path.concat(".");
?????path = path.concat(PathTool.getExtention(nameLoc));
????????
????
?????File?fl =?new?File(path);
????
?????return?fl.getCanonicalPath();//
}
?
以下是service層做的處理:
整體模塊劃分如下:
其中數(shù)據(jù)類(lèi)實(shí)體邏輯處理如下
public?class?FileInf?{
?
?????public?FileInf(){}
?????public?String?id="";
?????public?String?pid="";
????public?String?pidRoot="";???
?????/**??*?表示當(dāng)前項(xiàng)是否是一個(gè)文件夾項(xiàng)。????*/
?????public?boolean?fdTask=false;????????
?????//???///?是否是文件夾中的子文件??/// </summary>
?????public?boolean?fdChild=false;
?????/**??*?用戶(hù)ID。與第三方系統(tǒng)整合使用。????*/
?????public?int?uid=0;
?????/**??*?文件在本地電腦中的名稱(chēng)???*/
?????public?String?nameLoc="";
?????/**??*?文件在服務(wù)器中的名稱(chēng)。???*/
?????public?String?nameSvr="";
?????/**??*?文件在本地電腦中的完整路徑。示例:D:SoftQQ2012.exe?*/
?????public?String?pathLoc="";??
?????/**??*?文件在服務(wù)器中的完整路徑。示例:F:\ftp\uer\md5.exe?????*/
?????public?String?pathSvr="";
?????/**??*?文件在服務(wù)器中的相對(duì)路徑。示例:/www/web/upload/md5.exe???*/
?????public?String?pathRel="";
?????/**??*?文件MD5????*/
?????public?String?md5="";
?????/**??*?數(shù)字化的文件長(zhǎng)度。以字節(jié)為單位,示例:120125????*/
?????public?long?lenLoc=0;
?????/**??*?格式化的文件尺寸。示例:10.03MB???*/
?????public?String?sizeLoc="";
?????/**??*?文件續(xù)傳位置。??*/
?????public?long?offset=0;
?????/**??*?已上傳大小。以字節(jié)為單位?*/
?????public?long?lenSvr=0;
?????/**??*?已上傳百分比。示例:10%??*/
?????public?String?perSvr="0%";
?????public?boolean?complete=false;
?????public?Date?PostedTime?=?new?Date();
?????public?boolean?deleted=false;
?????/**??*?是否已經(jīng)掃描完畢,提供給大型文件夾使用,大型文件夾上傳完畢后開(kāi)始掃描。??*/
?????public?boolean?scaned=false;
}
后臺(tái)數(shù)據(jù)庫(kù)中的邏輯基本上都用到了上面的實(shí)體類(lèi)
文件數(shù)據(jù)表操作類(lèi)如下
加載所有未完成的文件列表
public?String?GetAllUnComplete(int?f_uid)
{
?????StringBuilder?sb =?new?StringBuilder();
?????sb.append("select ");
?????sb.append(" f_id");
?????sb.append(",f_fdTask");????
?????sb.append(",f_nameLoc");
?????sb.append(",f_pathLoc");
?????sb.append(",f_md5");
?????sb.append(",f_lenLoc");
?????sb.append(",f_sizeLoc");
?????sb.append(",f_pos");
?????sb.append(",f_lenSvr");
?????sb.append(",f_perSvr");
?????sb.append(",f_complete");
?????sb.append(",f_pathSvr");//fix(2015-03-16):修復(fù)無(wú)法續(xù)傳文件的問(wèn)題。
?????sb.append(" from up6_files ");//change(2015-03-18):聯(lián)合查詢(xún)文件夾數(shù)據(jù)
?????sb.append(" where f_uid=? and f_deleted=0 and f_fdChild=0 and f_complete=0 and f_scan=0");//fix(2015-03-18):只加載未完成列表
?
?????ArrayList<FileInf> files =?new?ArrayList<FileInf>();
?????DbHelper?db =?new?DbHelper();
?????PreparedStatement cmd = db.GetCommand(sb.toString());
?????try?{
?????????cmd.setInt(1, f_uid);
?????????ResultSet r = db.ExecuteDataSet(cmd);
?????????while(r.next())
?????????{
??????????????FileInf?f??????????=?new?FileInf();
??????????????f.uid??????????????= f_uid;
??????????????f.id???????????????= r.getString(1);
??????????????f.fdTask??????= r.getBoolean(2);??????????????
??????????????f.nameLoc??????????= r.getString(3);
??????????????f.pathLoc??????????= r.getString(4);
??????????????f.md5??????????????= r.getString(5);
??????????????f.lenLoc??????= r.getLong(6);
??????????????f.sizeLoc??????????= r.getString(7);
??????????????f.offset??????= r.getLong(8);
??????????????f.lenSvr??????= r.getLong(9);
??????????????f.perSvr??????= r.getString(10);
??????????????f.complete?????????= r.getBoolean(11);
??????????????f.pathSvr?????= r.getString(12);//fix(2015-03-19):修復(fù)無(wú)法續(xù)傳文件的問(wèn)題。
??????????????files.add(f);
?????????????
?????????}
?????????r.close();
?????????cmd.getConnection().close();
?????????cmd.close();
?????}?catch?(SQLException?e) {
?????????//?TODO?Auto-generated catch block
?????????e.printStackTrace();
?????}
????
?????if(files.size() < 1)?return?null;
????
?????Gson?g =?new?Gson();
????return?g.toJson( files);//bug:arrFiles為空時(shí),此行代碼有異常
}
實(shí)現(xiàn)后的整體效果如下
文件夾上傳完后的效果
服務(wù)器保存的文件夾數(shù)據(jù),而且層級(jí)結(jié)構(gòu)與本地客戶(hù)端是一致的。這在OA系統(tǒng)中,或者網(wǎng)盤(pán)系統(tǒng)中使用時(shí)是非常有用的
后端代碼邏輯大部分是相同的,目前能夠支持MySQL,Oracle,SQL。在使用前需要配置一下數(shù)據(jù)庫(kù),可以參考我寫(xiě)的這篇文章:
歡迎入群一起討論“374992201”
?
?
本文摘自 :https://www.cnblogs.com/