java利用webuploader实现超大文件分片上传、断点续传
我们平时经常做的是上传文件,上传文件夹与上传文件类似,但也有一些不同之处,这次做了上传文件夹就记录下以备后用。
首先我们需要了解的是上传文件三要素:
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:\Soft\QQ2012.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。在使用前需要配置一下数据库,可以参考我写的这篇文章: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/
java利用webuploader实现超大文件分片上传、断点续传的更多相关文章
- b/s利用webuploader实现超大文件分片上传、断点续传
本人在2010年时使用swfupload为核心进行文件的批量上传的解决方案.见文章:WEB版一次选择多个文件进行批量上传(swfupload)的解决方案. 本人在2013年时使用plupload为核心 ...
- 前端利用webuploader实现超大文件分片上传、断点续传
本人在2010年时使用swfupload为核心进行文件的批量上传的解决方案.见文章:WEB版一次选择多个文件进行批量上传(swfupload)的解决方案. 本人在2013年时使用plupload为核心 ...
- 利用webuploader实现超大文件分片上传、断点续传
之前仿造uploadify写了一个HTML5版的文件上传插件,没看过的朋友可以点此先看一下~得到了不少朋友的好评,我自己也用在了项目中,不论是用户头像上传,还是各种媒体文件的上传,以及各种个性的业务需 ...
- php利用webuploader实现超大文件分片上传、断点续传
PHP用超级全局变量数组$_FILES来记录文件上传相关信息的. 1.file_uploads=on/off 是否允许通过http方式上传文件 2.max_execution_time=30 允许脚本 ...
- jsp利用webuploader实现超大文件分片上传、断点续传
1,项目调研 因为需要研究下断点上传的问题.找了很久终于找到一个比较好的项目. 在GoogleCode上面,代码弄下来超级不方便,还是配置hosts才好,把代码重新上传到了github上面. http ...
- asp.net利用webuploader实现超大文件分片上传、断点续传
ASP.NET上传文件用FileUpLoad就可以,但是对文件夹的操作却不能用FileUpLoad来实现. 下面这个示例便是使用ASP.NET来实现上传文件夹并对文件夹进行压缩以及解压. ASP.NE ...
- thinkphp+webuploader实现大文件分片上传
大文件分片上传,简单来说就是把大文件切分为小文件,然后再一个一个的上传,到最后由这些小文件再合并成原来的文件 webuploader下载地址及其文档:http://fex.baidu.com/webu ...
- 在React中使用WebUploader实现大文件分片上传的踩坑日记!
前段时间公司项目有个大文件分片上传的需求,项目是用React写的,大文件分片上传这个功能使用了WebUploader这个组件. 具体交互是: 1. 点击上传文件button后出现弹窗,弹窗内有选择文件 ...
- 以寡治众各个击破,超大文件分片上传之构建基于Vue.js3.0+Ant-desgin+Tornado6纯异步IO高效写入服务
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_218 分治算法是一种很古老但很务实的方法.本意即使将一个较大的整体打碎分成小的局部,这样每个小的局部都不足以对抗大的整体.战国时期 ...
随机推荐
- 利用Python进行数据分析 第8章 数据规整:聚合、合并和重塑.md
学习时间:2019/11/03 周日晚上23点半开始,计划1110学完 学习目标:Page218-249,共32页:目标6天学完(按每页20min.每天1小时/每天3页,需10天) 实际反馈:实际XX ...
- 怎样让ssh连接保持连接, 而不会因为没有操作而中断
因为安全方面的考虑, ssh服务默认在一段时间内不操作会断开连接, 解决方法修改ssh的配置文件, 让ssh每隔一段时间就自动进行一次连接, 以达到保持连接的目的. 首先找到ssh配置文件的位置: f ...
- iOS - 数据存储方式(本地化)
iOS中数据存储方式 一般使用以下4种:(已更新) .NSKeyedAchiever//序列化 存放对象 .NSUserDefaults//本质是plist存储 NSData.NSString.NSN ...
- day01-02
- javascript--清除表单缓存
表单缓存是指当用户在表单输入之后再次回到该表单或者刷新页面的时候,浏览器会直接显示用户之前的输入,即表单缓存下来了.多数情况下这正是我们想要的,但也有些情况我们希望表单能够刷新,特别是根据后台的数据刷 ...
- iptables的nat规则骚操作
水一枪 我对防火墙这块的认知是比较低的, 之前一直没怎么去用 最多的要么就是 iptables -A INPUT -p tcp --dport 80 -j ACCEPT iptables -A OUT ...
- 理解JVM之java内存模型
java虚拟机规范中试图定义一种java内存模型(JMM)来屏蔽掉各种硬件和操作系统内存访问差异,以实现让java程序在各种平台都能打到一致的内存访问效果.所以java内存模型的主要目标是定义程序中各 ...
- List集合和Set集合互转
List集合转成Set集合(如果List集合的元素有重复,转成Set集合就会去掉重复的数据,每条数据只保留一条) //List转化成Set List<String> list = new ...
- Python 一些内置函数的总结~~~~
1. type() 两种用法 a. 当传入参数为一个时,返回值为参数的类型 b. 当传入参数为三个时,type(name, bases, dict) name: 类名 bases: 继承父类的元组,可 ...
- RT-Thread--内核移植
内核移植 内核移植就是指将 RT-Thread 内核在不同的芯片架构.不同的板卡上运行起来,能够具备线程管理和调度,内存管理,线程间同步和通信.定时器管理等功能.移植可分为 CPU 架构移植和 BSP ...