Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 429|回复: 0

JAVA大文件上传断点续传解决方案

[复制链接]
  • TA的每日心情
    奋斗
    前天 15:06
  • 签到天数: 802 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726006
    发表于 2021-5-10 16:38:16 | 显示全部楼层 |阅读模式

    javaweb上传文件

    上传文件的jsp中的部分

    上传文件同样可以使用form表单向后端发请求,也可以使用 ajax向后端发请求

        1.通过form表单向后端发送请求

             <form id="postForm" action="${pageContext.request.contextPath}/UploadServlet" method="post" enctype="multipart/form-data">

                <div class="bbxx wrap">

                    <inputtype="text" id="side-profile-name" name="username" class="form-control">

                    <inputtype="file" id="example-file-input" name="avatar">

                    <button type="submit" class="btn btn-effect-ripple btn-primary">Save</button>

                </div>

            </form>

    改进后的代码不需要form标签,直接由控件来实现。开发人员只需要关注业务逻辑即可。JS中已经帮我们封闭好了

        this.post_file = function ()

        {

            $.each(this.ui.btn, function (i, n) { n.hide();});

            this.ui.btn.stop.show();

            this.State = this.Config.state.Posting;//

            this.app.postFile({ id: this.fileSvr.id, pathLoc: this.fileSvr.pathLoc, pathSvr:this.fileSvr.pathSvr,lenSvr: this.fileSvr.lenSvr, fields: this.fields });

        };

    通过监控工具可以看到控件提交的数据,非常的清晰,调试也非常的简单。

    2.通过ajax向后端发送请求

                $.ajax({

                     url : "${pageContext.request.contextPath}/UploadServlet",

                     type : "POST",

                     data : $( '#postForm').serialize(),

                     success : function(data) {

                          $( '#serverResponse').html(data);

                     },

                     error : function(data) {

                          $( '#serverResponse').html(data.status + " : " + data.statusText + " : " + data.responseText);

                     }

                });

    ajax分为两部分,一部分是初始化,文件在上传前通过AJAX请求通知服务端进行初始化操作

        this.md5_complete = function (json)

        {

            this.fileSvr.md5 = json.md5;

            this.ui.msg.text("MD5计算完毕,开始连接服务器...");

            this.event.md5Complete(this, json.md5);//biz event

     

            var loc_path = encodeURIComponent(this.fileSvr.pathLoc);

            var loc_len = this.fileSvr.lenLoc;

            var loc_size = this.fileSvr.sizeLoc;

            var param = jQuery.extend({}, this.fields, this.Config.bizData, { md5: json.md5, id: this.fileSvr.id, lenLoc: loc_len, sizeLoc: loc_size, pathLoc: loc_path, time: new Date().getTime() });

     

            $.ajax({

                type: "GET"

                , dataType: 'jsonp'

                , jsonp: "callback"//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名

                , url: this.Config["UrlCreate"]

                , data: param

                , success: function (sv)

                {

                    _this.svr_create(sv);

                }

                , error: function (req, txt, err)

                {

                    _this.Manager.RemoveQueuePost(_this.fileSvr.id);

                    alert("向服务器发送MD5信息错误!" + req.responseText);

                    _this.ui.msg.text("向服务器发送MD5信息错误");

                    _this.ui.btn.cancel.show();

                    _this.ui.btn.stop.hide();

                }

                , complete: function (req, sta) { req = null; }

            });

        };

     

    在文件上传完后向服务器发送通知

        this.post_complete = function (json)

        {

            this.fileSvr.perSvr = "100%";

            this.fileSvr.complete = true;

            $.each(this.ui.btn, function (i, n)

            {

                n.hide();

            });

            this.ui.process.css("width""100%");

            this.ui.percent.text("(100%)");

            this.ui.msg.text("上传完成");

            this.Manager.arrFilesComplete.push(this);

            this.State = this.Config.state.Complete;

            //从上传列表中删除

            this.Manager.RemoveQueuePost(this.fileSvr.id);

            //从未上传列表中删除

            this.Manager.RemoveQueueWait(this.fileSvr.id);

     

            var param = { md5: this.fileSvr.md5, uid: this.uid, id: this.fileSvr.id, time: new Date().getTime() };

     

            $.ajax({

                type: "GET"

                  , dataType: 'jsonp'

                  , jsonp: "callback"//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名

                  , url: _this.Config["UrlComplete"]

                  , data: param

                  , success: function (msg)

                  {

                      _this.event.fileComplete(_this);//触发事件

                      _this.post_next();

                  }

                  , error: function (req, txt, err) { alert("文件-向服务器发送Complete信息错误!" + req.responseText); }

                  , complete: function (req, sta) { req = null; }

            });

        };

     

    这里需要处理一个MD5秒传的逻辑,当服务器存在相同文件时,不需要用户再上传,而是直接通知用户秒传

        this.post_complete_quick = function ()

        {

            this.fileSvr.perSvr = "100%";

            this.fileSvr.complete = true;

            this.ui.btn.stop.hide();

            this.ui.process.css("width""100%");

            this.ui.percent.text("(100%)");

            this.ui.msg.text("服务器存在相同文件,快速上传成功。");

            this.Manager.arrFilesComplete.push(this);

            this.State = this.Config.state.Complete;

            //从上传列表中删除

            this.Manager.RemoveQueuePost(this.fileSvr.id);

            //从未上传列表中删除

            this.Manager.RemoveQueueWait(this.fileSvr.id);

            //添加到文件列表

            this.post_next();

            this.event.fileComplete(this);//触发事件

        };

    这里可以看到秒传的逻辑是非常 简单的,并不是特别的复杂。

                var form = new FormData();

                form.append("username","zxj");

                form.append("avatar",file);

                //var form = new FormData($("#postForm")[0]);

                $.ajax({

                    url:"${pageContext.request.contextPath}/UploadServlet",

                    type:"post",

                    data:form,

                    processData:false,

                    contentType:false,

                    success:function(data){

            

                        console.log(data);

                    }

                });

    java部分

    文件初始化的逻辑,主要代码如下

    FileInf fileSvr= new FileInf();

    fileSvr.id = id;

    fileSvr.fdChild = false;

    fileSvr.uid = Integer.parseInt(uid);

    fileSvr.nameLoc = PathTool.getName(pathLoc);

    fileSvr.pathLoc = pathLoc;

    fileSvr.lenLoc = Long.parseLong(lenLoc);

    fileSvr.sizeLoc = sizeLoc;

    fileSvr.deleted = false;

    fileSvr.md5 = md5;

    fileSvr.nameSvr = fileSvr.nameLoc;

     

    //所有单个文件均以uuid/file方式存储

    PathBuilderUuid pb = new PathBuilderUuid();

    fileSvr.pathSvr = pb.genFile(fileSvr.uid,fileSvr);

    fileSvr.pathSvr = fileSvr.pathSvr.replace("\\","/");

     

    DBConfig cfg = new DBConfig();

    DBFile db = cfg.db();

    FileInf fileExist = new FileInf();

        

    boolean exist = db.exist_file(md5,fileExist);

    //数据库已存在相同文件,且有上传进度,则直接使用此信息

    if(exist && fileExist.lenSvr > 1)

    {

         fileSvr.nameSvr             = fileExist.nameSvr;

         fileSvr.pathSvr        = fileExist.pathSvr;

         fileSvr.perSvr              = fileExist.perSvr;

         fileSvr.lenSvr              = fileExist.lenSvr;

         fileSvr.complete       = fileExist.complete;

         db.Add(fileSvr);

        

         //触发事件

        up6_biz_event.file_create_same(fileSvr);

    }//此文件不存在

    else

    {

         db.Add(fileSvr);

         //触发事件

        up6_biz_event.file_create(fileSvr);

        

         FileBlockWriter fr = new FileBlockWriter();

         fr.CreateFile(fileSvr.pathSvr,fileSvr.lenLoc);

    }

    接收文件块数据,在这个逻辑中我们接收文件块数据。控件对数据进行了优化,可以方便调试。如果用监控工具可以看到控件提交的数据。

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

         }

    }

     

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

     

    注:

    1. 上面的java部分的代码可以直接使用,只需要将上传的图片路径及收集数据并将数据写入到数据库即可

    2. 上面上传文件使用到了字节流,其实还可以使用别的流,这个需要读者自己在下面完善测试

    3. BeanUtils是一个工具 便于将实体对应的属性赋给实体

    4. 上传文件不能使用 request.getParameter("")获取参数了,而是直接将request解析,通过判断每一项是文件还是非文件,然后进行相应的操作(文件的话就是用流来读取,非文件的话,暂时保存到一个map中。)

    后端代码逻辑大部分是相同的,目前能够支持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/


    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-11-13 07:43 , Processed in 2.080468 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表