JSP WEB项目大文件上传下载解决方案

javaweb上传文件

上传文件的jsp中的部分

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

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

 Save

改进后的代码不需要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。在使用前需要配置一下数据库,详细源码可以直接百度搜索“up6”

欢迎入群一起讨论“374992201”

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容