javahttp大文件断点续传上传组件

松鼠的博客      2022-04-05     302

关键词:

我们平时经常做的是上传文件,上传文件夹与上传文件类似,但也有一些不同之处,这次做了上传文件夹就记录下以备后用。

首先我们需要了解的是上传文件三要素:

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:ftpuermd5.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。在使用前需要配置一下数据库,可以参考我写的这篇文章:http://blog.ncmem.com/wordpress/2019/08/12/java-http%E5%A4%A7%E6%96%87%E4%BB%B6%E6%96%AD%E7%82%B9%E7%BB%AD%E4%BC%A0%E4%B8%8A%E4%BC%A0/

欢迎入群一起讨论“374992201”

完整版断点续传秒传,支持超大大大文件_支持重定义文件名和路径

需求:支持大文件批量上传(20G)和下载,同时需要保证上传期间用户电脑不出现卡死等体验;内网百兆网络上传速度为12MB/S服务器内存占用低支持文件夹上传,文件夹中的文件数量达到1万个以上,且包含层级结构。支持PC端全... 查看详情

ios大文件分片上传和断点续传

总结一下大文件分片上传和断点续传的问题。因为文件过大(比如1G以上),必须要考虑上传过程网络中断的情况。http的网络请求中本身就已经具备了分片上传功能,当传输的文件比较大时,http协议自动会将文件切片(分块)... 查看详情

javascript之大文件分段上传断点续传

​需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在500M内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以501M来进行限制。第一步:前端修改由于项目使用的是BJUI前端框架,并... 查看详情

html超大文件上传和断点续传的实现

需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在500M内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以501M来进行限制。 第一步:前端修改由于项目使用的是BJUI前端框架,... 查看详情

支持ie低版本的上传大文件切割上传断点续传秒传

...swfupload2、http://download.csdn.net/detail/rememberme001/9873136支持大文件传输,先把大文件分割成每个2M的小文件分批上传,再组合成一个大文件。支持断点续传,MD5校验实现妙传功能,支 查看详情

vuehttp大文件断点续传上传

javaweb上传文件上传文件的jsp中的部分上传文件同样可以使用form表单向后端发请求,也可以使用ajax向后端发请求    1.通过form表单向后端发送请求         <formid="postForm"action="$pageCo... 查看详情

vue解决大文件断点续传

​需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在500M内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以501M来进行限制。第一步:前端修改由于项目使用的是BJUI前端框架,并... 查看详情

springboot实现分片上传断点续传大文件极速秒传-备忘(代码片段)

  文件上传是一个老生常谈的话题了,在文件相对比较小的情况下,可以直接把文件转化为字节流上传到服务器,但在文件比较大的情况下,用普通的方式进行上传,这可不是一个好的办法,毕竟很少有人会忍受,当文件上传... 查看详情

大文件分片上传,断点续传,秒传实现

...一般的HTTP请求发送数据的方式的话,会遇到的问题:1,文件过大,超出服务端的请求大小限制;2,请求时间过长,请求超时;3,传输中断,必须重新上传导致前功尽弃; 解决方案:1,修改服务端上传的限制配置;Nginx以... 查看详情

如何用xmlhttprequest实现大文件上传和断点续传

...象,能够实现断店续传。我说一下我的思路吧:首先获取文件的MD5(spark-md5.js),然后利用文件对象的slice方法进行切割文件,分段上传到后台,后台组装文件然后校验MD5值;我也刚好在做,觉得可行本回答被提问者采纳 查看详情

html5大文件断点续传完整思路整理

需求:支持大文件批量上传(20G)和下载,同时需要保证上传期间用户电脑不出现卡死等体验;内网百兆网络上传速度为12MB/S服务器内存占用低支持文件夹上传,文件夹中的文件数量达到1万个以上,且包含层级结构。支持PC端全... 查看详情

项目难点——断点续传分片上传(代码片段)

项目难点——【2】断点续传、分片上传1文件分片在网络请求中,如果我们有时是上传大文件,可能由于网络原因,导致上传断断续续,很难一次性上传成功,那么这个时候我们就需要将大文件分块,分成... 查看详情

面试官:请你实现一个大文件上传和断点续传(代码片段)

...花了一段时间整理了下思路,那么究竟该如何实现一个大文件上传,以及在上传中如何实现断点续传的功能呢?本文将从零搭建前端和服务端,实现一个大文件上传和断点续 查看详情

b/s之大文件分段上传断点续传

4GB以上超大文件上传和断点续传服务器的实现随着视频网站和大数据应用的普及,特别是高清视频和4K视频应用的到来,超大文件上传已经成为了日常的基础应用需求。但是在很多情况下,平台运营方并没有大文件上传和断点续... 查看详情

超大文件上传和断点续传的源代码

总结一下大文件分片上传和断点续传的问题。因为文件过大(比如1G以上),必须要考虑上传过程网络中断的情况。http的网络请求中本身就已经具备了分片上传功能,当传输的文件比较大时,http协议自动会将文件切片(分块)... 查看详情

前端实现大文件上传分片上传断点续传

总结一下大文件分片上传和断点续传的问题。因为文件过大(比如1G以上),必须要考虑上传过程网络中断的情况。http的网络请求中本身就已经具备了分片上传功能,当传输的文件比较大时,http协议自动会将文件切片(分块)... 查看详情

webuploader与django进行断点续传,大文件上传(代码片段)

需要实现的效果如下需要使用的 jsjquery.jswebuploader.hshashmap.js路由fromdjango.urlsimportpathfrom.importviewsurlpatterns=[path('index/',views.index),path('checkChunk/',views.checkChunk,name=&# 查看详情

java大文件断点续传

java两台服务器之间,大文件上传(续传),采用了Socket通信机制以及JavaIO流两个技术点,具体思路如下:实现思路:1、服:利用ServerSocket搭建服务器,开启相应端口,进行长连接操作2、服:使用ServerSocket.accept()方法进行阻塞,... 查看详情