• 當前位置:首頁 > IT技術 > Web編程 > 正文

    WebUploader上傳超大文件和斷點續傳示例
    2021-10-22 16:43:22

    ?

    我們平時經常做的是上傳文件,上傳文件夾與上傳文件類似,但也有一些不同之處,這次做了上傳文件夾就記錄下以備后用。

    首先我們需要了解的是上傳文件三要素:

    1.表單提交方式:post (get方式提交有大小限制,post沒有)

    2.表單的enctype屬性:必須設置為multipart/form-data.

    3.表單必須有文件上傳項:file,且文件項需要給定name值

    上傳文件夾需要增加一個屬性webkitdirectory,像這樣:

    <input id="fileFolder" name="fileFolder" type="file"??webkitdirectory>

    不過webkitdirectory屬性有個問題,只能支持高版本的chrome,不能支持低版本的IE,如ie6,ie7,ie8,不能做到全瀏覽器適配,運行環境比較單一。

    js中可以判斷文件夾中文件數量及文件夾大小是否符合要求,不符合要求不能向后臺提交:

    前臺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程序開發.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="繼續"><img name="post" src="js/post.png"/><div>繼續</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程序開發.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="繼續"><img name="post" src="js/post.png"/><div>繼續</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">選擇多個文件</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]);

    ?????}

    };

    后臺在接收文件夾時不同之處在需要用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)

    {//?解析文件數據錯誤?

    ????out.println("read file data error:"?+ e.toString());

    ????return;

    ??

    }

    ?

    FileItem rangeFile =?null;

    //?得到所有上傳的文件

    Iterator fileItr = files.iterator();

    //?循環處理所有文件

    while?(fileItr.hasNext())

    {

    ?????//?得到當前文件

    ?????rangeFile = (FileItem) fileItr.next();

    ?????if(StringUtils.equals( rangeFile.getFieldName(),"pathSvr"))

    ?????{

    ?????????pathSvr = rangeFile.getString();

    ?????????pathSvr = PathTool.url_decode(pathSvr);

    ?????}

    }

    ?

    server端的包和類

    ?

    ?

    文件塊處頁面,驗證代碼部分

    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)

    {

    ?????//保存文件塊數據

    ?????FileBlockWriter res =?new?FileBlockWriter();

    ?????//僅第一塊創建

    ?????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);

    ?

    生成文件名稱的邏輯

    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層做的處理:

    整體模塊劃分如下:

    ?

    其中數據類實體邏輯處理如下

    public?class?FileInf?{

    ?

    ?????public?FileInf(){}

    ?????public?String?id="";

    ?????public?String?pid="";

    ????public?String?pidRoot="";???

    ?????/**??*?表示當前項是否是一個文件夾項。????*/

    ?????public?boolean?fdTask=false;????????

    ?????//???///?是否是文件夾中的子文件??/// </summary>

    ?????public?boolean?fdChild=false;

    ?????/**??*?用戶ID。與第三方系統整合使用。????*/

    ?????public?int?uid=0;

    ?????/**??*?文件在本地電腦中的名稱???*/

    ?????public?String?nameLoc="";

    ?????/**??*?文件在服務器中的名稱。???*/

    ?????public?String?nameSvr="";

    ?????/**??*?文件在本地電腦中的完整路徑。示例:D:SoftQQ2012.exe?*/

    ?????public?String?pathLoc="";??

    ?????/**??*?文件在服務器中的完整路徑。示例:F:\ftp\uer\md5.exe?????*/

    ?????public?String?pathSvr="";

    ?????/**??*?文件在服務器中的相對路徑。示例:/www/web/upload/md5.exe???*/

    ?????public?String?pathRel="";

    ?????/**??*?文件MD5????*/

    ?????public?String?md5="";

    ?????/**??*?數字化的文件長度。以字節為單位,示例:120125????*/

    ?????public?long?lenLoc=0;

    ?????/**??*?格式化的文件尺寸。示例:10.03MB???*/

    ?????public?String?sizeLoc="";

    ?????/**??*?文件續傳位置。??*/

    ?????public?long?offset=0;

    ?????/**??*?已上傳大小。以字節為單位?*/

    ?????public?long?lenSvr=0;

    ?????/**??*?已上傳百分比。示例:10%??*/

    ?????public?String?perSvr="0%";

    ?????public?boolean?complete=false;

    ?????public?Date?PostedTime?=?new?Date();

    ?????public?boolean?deleted=false;

    ?????/**??*?是否已經掃描完畢,提供給大型文件夾使用,大型文件夾上傳完畢后開始掃描。??*/

    ?????public?boolean?scaned=false;

    }

    后臺數據庫中的邏輯基本上都用到了上面的實體類

    文件數據表操作類如下

    加載所有未完成的文件列表

    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):修復無法續傳文件的問題。

    ?????sb.append(" from up6_files ");//change(2015-03-18):聯合查詢文件夾數據

    ?????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):修復無法續傳文件的問題。

    ??????????????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為空時,此行代碼有異常

    }

    實現后的整體效果如下

    ?

    文件夾上傳完后的效果

    ?

    服務器保存的文件夾數據,而且層級結構與本地客戶端是一致的。這在OA系統中,或者網盤系統中使用時是非常有用的

    ?

    后端代碼邏輯大部分是相同的,目前能夠支持MySQL,Oracle,SQL。在使用前需要配置一下數據庫,可以參考我寫的這篇文章:java http大文件斷點續傳上傳 – 澤優軟件博客

    歡迎入群一起討論“374992201”

    ?

    ?

    本文摘自 :https://www.cnblogs.com/

    開通會員,享受整站包年服務
    国产呦精品一区二区三区网站|久久www免费人咸|精品无码人妻一区二区|久99久热只有精品国产15|中文字幕亚洲无线码