chunkupload 文件上传断点续传组件(java) - 正式发布
chunkupload简介
chunkupload是一款基于java语言的断点续传组件,针对文件上传,非文件下载,集成方便,使用简单。
chunkupload实现如下功能:
· 实现断点续传
· 对于同一个文件,允许多用户同时上传,并且上传的用户越多,上传越快
· 线程安全
· 同一物理机下进程安全
· 文件自动切片,支持合并
· 内存占用小
· 高效稳定,高可用
· 易集成,无第三方依赖
chunkupload只关注文件上传,并没有安全机制,开发者需要自行设计安全控制策略,防范用户上传非法文件,chunkupload默认上传的文件是安全的。
chunkupload功能完备,服务端和客户端无缝衔接,开发者只需关注自身业务和UI展现即可。
为了尽可能提升用户体验,chunkupload在客户端的技术选型有些激进,采用了许多先进的技术,比如:web worker、XMLHttpRequest数据传送进度、FileReader、file slice等,所以对浏览器兼容性会有一定影响,在确定使用chunkupload前请务必仔细斟酌!
chunkupload集成
服务端
chunkupload服务端运行需要JRE7或更高版本,无任何第三方依赖。
1.引用chunkupload.1.0.jar。
2.在项目web.xml中配置chunkupload servlet。
<servlet>
<servlet-name>ChunkUpload</servlet-name>
<servlet-class>com.iyangyuan.chunkupload.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ChunkUpload</servlet-name>
<url-pattern>/chunkupload/*</url-pattern>
</servlet-mapping>
强烈建议将chunkupload servlet配置在安全框架之后(比如shiro),mvc框架之前(如springmvc)。
chunkupload servlet中的拦截路径(url-pattern),如果无特殊需求,无需更改,假如一定要更改,还需要同步更改客户端的配置。
客户端
chunkupload客户端对浏览器的要求比较高,目前已知chrome、firefox浏览器完整支持,360浏览器如果启用webkit内核,应该也没有问题,IE浏览器绝对不支持(想都不要想),其他浏览器未知。
开发者可能会问,为什么兼容性如此捉襟见肘?
chunkupload是新时代的产物,它代表了时代的发展方向,它的价值在于提供最先进的技术示范,而不是沉重的历史包袱。
chunkupload在客户端的实现,无任何第三方依赖,只关注与服务端的逻辑交互,并不干预UI展现,为开发者创造最大的发挥空间。
1.引用dawn.1.0.js,用于在客户端计算文件MD5,dawn.js是chunkupload的一个附属项目,相比未经优化的javascript计算MD5方法,dawn.js将计算效率提升50%左右。
2.引用chunkupload.1.0.js,此乃chunkupload客户端核心库,封装了所有上传所需的逻辑。
chunkupload使用
服务端
服务端需要创建chunkupload.properties配置文件,放置在项目classpath根目录下,也就是大家熟悉的log4j.properties所在目录,配置文件中有如下选项:
· root 文件存储路径,相当于根目录,内部还会有chunkupload创建的目录结构;假如同一台物理机配置多个文件上传容器,此项配置应该设置成统一目录,默认为[/data]。
· fileLockCapacity 文件锁缓存容量,一般设置为2048即可,开发者可根据服务器性能自行调整,默认为2048。
· createFile 文件上传完成后,是否立即合并切片,生成完整文件;强烈建议此配置项设置为false,一般情况下,切片无需合并,就算合并,也不需要立即合并;如果设为true,立即合并文件会占用大量服务器资源,并且会造成客户端长时间等待;合并的速度大约100M/S,视服务器具体性能而定,默认为true。
至此,服务端已经可以正常运作了。
客户端
默认情况下,客户端无需任何配置。
假如开发者更改过chunkupload servlet拦截路径,那么chunkupload.1.0.js中的Block.config.api配置也需要做相应的改动,具体情况需要开发者自行斟酌。
ChunkUpload 类
文件上传核心实现类。
实例化
上传组件初始化需要提供目标文件。
/** * 实例化ChunkUpload组件 * file 要上传的目标文件对象 */ var cu = new ChunkUpload(file);
upload 方法
upload 方法用来启动文件上传,通过四个异步回调完成上传交互,无返回值。
cu.upload({
"success": function(block){
/**
* 上传成功回调
*
* block 对象,块对象
*/
},
"error": function(e){
/**
* 上传异常回调
*
* e 字符串,异常信息
*/
},
"md5Progress": function(n){
/**
* 计算文件md5进度回调
*
* n 整型,进度数值
*/
},
"uploadProgress": function(n){
/**
* 上传进度回调
*
* n 整型,进度数值
*/
}
});
abort方法
abort方法用来中断上传,可以在任意阶段任意时刻中断,无返回值。
cu.abort();
Block 类
文件控制类。
实例化
/** * 初始化块对象 * md5 文件md5,32位 * size 文件大小,字节 */ var block = new Block(md5, size);
info方法
获取块(文件)信息,返回javascript对象。
block.info();
返回示例:
{ "status": 0, //业务状态,0表示成功 "data": { //数据域 "chunks": [ //所有切片信息 { "md5": "e114c21f7d9f8ad1a8551225c3d085be", //切片md5 "n": 1 //切片序号 }, { "md5": "48357caa7607a636e858315e1b0216d5", "n": 2 }, { "md5": "a23c6ab7104d2ce4ae3c1624ea7eab55", "n": 3 }, { "md5": "3eb29f6241d6fbb35cc715fff2b9ab91", "n": 4 }, { "md5": "120ddc96b878a63adcd7835cbac0c95c", "n": 5 } ], "chunkNum": 5, //切片数量 "md5": "f1154ca6fab7f3628927c1268f3570fd", //文件md5 "state": 1, //文件状态,1为上传完成 "size": 20879935 //文件长度 } }
delete方法
删除块(文件) ,无返回值。
block.delete();
chunkupload服务端存储珠玑
任何上传的文件都会在服务端进行切片处理,每个切片4M大小。
通过文件MD5和文件大小,唯一确定一个文件。
目录分散策略,基于开发者自定义的rootpath,文件MD5前6位,每两位作为一级目录,最后以文件MD5+文件长度作为最终目录,所有文件信息均存储在此目录下。
假如文件MD5为[071287fffa974b878732a7a17858be36],长度为[20879935],开发者自定义的rootpath为[/data],那么生成的目录结构为:[/data/07/12/87/071287fffa974b878732a7a17858be3620879935]。
chunkupload存储的关于文件的所有信息,均为二进制文件,并且文件名称固定,具体组织如下图:
chunkupload未来
展望chunkupload,未来无疑是开源的,只不过现在还不是时候,因为作者觉得它还不够完美。
通过大家的宝贵意见、建议,作者会不断完善、改进chunkupload,等到chunkupload成熟时,也就是开源之日!
希望大家多多与我交流~
chunkupload组件下载
你可以下载如下内容:
· chunkupload.jar
· chunkupload.js
· dawn.js
· 脚手架(集成了chunkupload的空白项目)
附:客户端使用示例
上传示例
<html>
<head>
<title>ChunkUpload 文件上传示例</title>
<meta charset="utf-8">
<meta name="viewport" id="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0">
<style>
body{
font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
body > div {
width: 200px;
margin: 20px auto 0 auto;
}
body > div.form {
text-align: center;
}
body > div.form > input {
margin-top: 12px;
border: 1px dashed #dcdcdc;
padding: 4px 8px;
cursor: pointer;
background-color: transparent;
color: #686868;
font-family: inherit;
outline: none;
width: 100%;
box-sizing: border-box;
}
body > div.form > input.button {
font-size: 15px;
line-height: 29px;
padding: 0 10px;
}
body > div.form > input:hover {
background-color: #f5f5f5;
}
div.info > div{
margin: auto;
height: 31px;
width: 88px;
background: url('data:image/gif;base64,R0lGODlhWAAfAMMBAAAAAP////XcoPDLdfTboP+dzv+z2f+AwP9brfPZoAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFFAAKACwAAAAAWAAfAAAE/xDISau9OOvNd/hgKI5kaZ5oWkqB4L5wLM90bd+4PLB57//A2g7QChqPyNiwmGw6c8undKrj/STULC3qAxQAMKw0IUiYyy6yWf3j5rwGsMsrd5JfdzQelre5bxIFcROCdUBneWx6aHd9UFY1hAaTAJQTRo6LfYl8i0KQMoGCk6Slg0FsnGmdq3uZSpBiAnCmtaWGqK4yiq2eVURzwbS2pBK3hzabezOvLlxyYMPEg5aXWk1uWNLTxaOU10lR2oHckwcHlIWn4Ed/c97ECOfocJWy7EDus/C28udx8HAlaZYP1ItttRAgKEYBGS9NelL10LevnC2BR1RhM3iwAD9uGE5/NBvpg+IcLx5BhvTxiiDBGSYPivJI0xo+JzHfZfjyxNHDGC9/MaFhb0bRmzg5hoqE9EnOpk2XDJhKtarVq1izat3KtSvWDmDDih0LNgIAIfkEBRQACgAsAwAGADEAFgAABJ9QyEmrBMDqzbsFRTZh3pacZQUaogC2qZRQc4oVLLbCaS2XOpyBNdTxYjPf5iYcOp+kmCfqaj6vUClHlFlhr5is9nOpfsGAonGs6lrPUOvR1r3BnYdDEZemskdvWAh5ejs5fyqBV4N5LHKIiXcGCAhORnMmAkoUXpJgkEsFimeYPVsgond+oEtMoq86rJpTIZdBpbIjLBp9spu8W7nCshEAIfkEBRQACgAsAwAFADoAFgAABLdQyEmrAMDqzWXySdhZQJFN2KhS30oWxnmZrtp++IrB2B7XNmAPZogVezKgJidobXbEonSaUjaZE2erOpt6vz+gk5VFSTIlsBdDFTffnRM6qo4Bjkirbl4Hp6VJHTdxaD59BgcHRzx2ei5/dQiJiml3XI5xdGCSiTF0gZhPml8ICIB5oSqQh22pjwWjdaAuOWNjJCWwfZeuWoS5sME9rh4VtxolSMo0vXqWyGHESyOzZ9LHx9LaAhEAIfkEBRQACgAsAgAFAEQAFgAABMtQyEmrvTjrmmT63VYBgGieVIhSQFFO5CpPYWefrfEK7T5fto+g9iMVdKSc75dqOlWZ5NGgoyaXK6HqNvR4vxbjlEoux2ZQcHrL2uXK8DhWdGODo5LSO24GmJlqXSIvemN8Vn5ISVkaaRgxe4dwkXOAGpBGkmQHB1ZHfmeWRYZ8CJydSkiiTJGlpzqGlauDpHEICGRXsrMbrZq5THVPXcJhBbWSu2g0MmLIucona4BSx9YuoaKOzBbbMNi6RtHaRToXoLyO3nfneLw/EQAh+QQFFAAKACwCAAUASwAXAAAE3lDISau9OOtdE/9gKI4aAJDo5qUSUJyTyVJeYgtrfeesa8ACF5C1khSLuEkNZSr8TL4haKdMWpM8o/YDdRp+X6iUg9xer2VR0/ttu2Wh5bFKP5eAPrd+P55W00t2FjAneXtvAG9xGkhzHIRBbIdhiU9QM5gxhZKTb5J9mSAyXZ1tBwdhTolwoZiGkwinqFFPrZmvh7GnP5+haTSinHsICG1ioBdywFo8WRu4pcaZjslqBcKdyGQYv2ZcLtelrCS/3YKia9fql7btgy/HXdofZYHVTD8Xq+78FMjz/TBEAAA7') no-repeat center center;
padding: 2px;
box-sizing: border-box;
}
div.info > div > p{
text-align: right;
font-size: 12px;
margin: 0;
}
</style>
</head>
<body>
<!-- 表单部分 -->
<div class="form">
<input id="bigFile" type="file" placeholder="选择一个文件" />
<input id="execBtn" class="button" type="button" value="上传" />
<input id="cancelBtn" class="button" type="button" value="取消" />
</div>
<!-- 上传进度展示 -->
<div class="info">
<div>
<!-- 提示文本 -->
<p id="text"></p>
<!-- 执行进度 -->
<p id="progress"></p>
</div>
</div>
</body>
<script src="/static/lib/dawn/dawn.js"></script>
<script src="/static/lib/chunkupload/chunkupload.js"></script>
<script>
var fileInput = document.getElementById("bigFile"),
execBtn = document.getElementById("execBtn"),
cancelBtn = document.getElementById("cancelBtn"),
text = document.getElementById("text"),
progress = document.getElementById("progress"),
cu; execBtn.addEventListener("click", function(e) {
var file;
file = fileInput.files[0];
cu = new ChunkUpload(file);
cu.upload({
"success": function(block){
/**
* 上传成功后,会返回一个block对象
* 通过block对象,可以在一定程度上管理文件,目前支持:
* 获取文件信息(参见info_example.html)
* 删除文件(参见delete_example.html)
*/
text.innerText = "上传完成";
},
"error": function(e){
text.innerText = e;
progress.innerText = "";
},
"md5Progress": function(n){
text.innerText = "计算MD5";
progress.innerText = n + "%";
},
"uploadProgress": function(n){
text.innerText = "正在上传";
progress.innerText = n + "%";
}
});
}); cancelBtn.addEventListener("click", function(e) {
if(cu){
cu.abort();
alert("已经取消!");
}
});
</script>
</html>
获取文件信息示例
<html>
<head>
<title>ChunkUpload 获取文件信息示例</title>
<meta charset="utf-8">
<meta name="viewport" id="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0">
<style>
body {
font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
body > div {
width: 200px;
margin: 20px auto 0 auto;
}
body > div.form {
color: #686868;
font-size: 15px;
text-align: center;
}
body > div.form > p {
margin: 1em 0 .4em 0;
text-align: left;
}
body > div.form > input {
background-color: transparent;
outline: none;
font-family: inherit;
font-size: inherit;
color: inherit;
}
body > div.form > input:hover {
background-color: #f5f5f5;
}
body > div.form > input.text {
padding: 6px 8px;
border: 1px solid #dcdcdc;
}
body > div.form > input.button {
border: 1px dashed #dcdcdc;
cursor: pointer;
padding: 0 10px;
line-height: 29px;
}
div.info{
width: 400px;
background-color: #f7faff;
border: 1px solid #b2d7ff;
padding: 10px 8px;
}
div.info > p{
margin: 0;
text-align: left;
font-size: 14px;
word-break: break-all;
}
</style>
</head>
<body>
<!-- 表单部分 -->
<div class="form">
<p>文件md5</p>
<input id="md5" class="text" type="text" placeholder="文件md5" />
<p>文件大小(字节)</p>
<input id="size" class="text" type="text" placeholder="文件大小(字节)" />
<p></p>
<input id="execBtn" class="button" type="button" value="获取" />
</div>
<!-- 结果展示 -->
<div class="info">
<p id="text">
</p>
</div>
</body>
<script src="/static/lib/dawn/dawn.js"></script>
<script src="/static/lib/chunkupload/chunkupload.js"></script>
<script>
var execBtn = document.getElementById("execBtn"),
md5 = document.getElementById("md5"),
size = document.getElementById("size"),
text = document.getElementById("text"); execBtn.addEventListener("click", function(e) {
var block = new Block(md5.value, size.value); block.info(function(info){
text.innerText = JSON.stringify(info);
},function(status, text){
text.innerText = "哎呀!出错啦," + text;
});
});
</script>
</html>
删除文件示例
<html>
<head>
<title>ChunkUpload 删除文件示例</title>
<meta charset="utf-8">
<meta name="viewport" id="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0">
<style>
body {
font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
body > div {
width: 200px;
margin: 20px auto 0 auto;
}
body > div.form {
color: #686868;
font-size: 15px;
text-align: center;
}
body > div.form > p {
margin: 1em 0 .4em 0;
text-align: left;
}
body > div.form > input {
background-color: transparent;
outline: none;
font-family: inherit;
font-size: inherit;
color: inherit;
}
body > div.form > input:hover {
background-color: #f5f5f5;
}
body > div.form > input.text {
padding: 6px 8px;
border: 1px solid #dcdcdc;
}
body > div.form > input.button {
border: 1px dashed #dcdcdc;
cursor: pointer;
padding: 0 10px;
line-height: 29px;
}
div.info{
width: 400px;
background-color: #f7faff;
border: 1px solid #b2d7ff;
padding: 10px 8px;
}
div.info > p{
margin: 0;
text-align: left;
font-size: 14px;
word-break: break-all;
}
</style>
</head>
<body>
<!-- 表单部分 -->
<div class="form">
<p>文件md5</p>
<input id="md5" class="text" type="text" placeholder="文件md5" />
<p>文件大小(字节)</p>
<input id="size" class="text" type="text" placeholder="文件大小(字节)" />
<p></p>
<input id="execBtn" class="button" type="button" value="删除" />
</div>
<!-- 结果展示 -->
<div class="info">
<p id="text">
</p>
</div>
</body>
<script src="/static/lib/dawn/dawn.js"></script>
<script src="/static/lib/chunkupload/chunkupload.js"></script>
<script>
var execBtn = document.getElementById("execBtn"),
md5 = document.getElementById("md5"),
size = document.getElementById("size"),
text = document.getElementById("text"); execBtn.addEventListener("click", function(e) {
var block = new Block(md5.value, size.value); block.delete(function(info){
text.innerText = JSON.stringify(info);
},function(status, text){
text.innerText = "哎呀!出错啦," + text;
});
});
</script>
</html>
chunkupload 文件上传断点续传组件(java) - 正式发布的更多相关文章
- chunkupload文件上传断点续传组件(java)
chunkupload简介 chunkupload是一款基于java语言的断点续传组件,针对文件上传,非文件下载,集成方便,使用简单. 从整体上讲,chunkupload会对文件进行切片处理,每个切片 ...
- Asp.net mvc 大文件上传 断点续传
Asp.net mvc 大文件上传 断点续传 进度条 概述 项目中需要一个上传200M-500M的文件大小的功能,需要断点续传.上传性能稳定.突破asp.net上传限制.一开始看到51CTO上的这 ...
- 文件上传plupload组件使用
这段时间一直在使用文件上传,简要的介绍一下文件上传的组件使用,先上一段代码. var uploader = new plupload.Uploader( { //用来指定上传方式,指定多个上传方式请使 ...
- java文件上传下载组件
需求: 支持大文件批量上传(20G)和下载,同时需要保证上传期间用户电脑不出现卡死等体验: 内网百兆网络上传速度为12MB/S 服务器内存占用低 支持文件夹上传,文件夹中的文件数量达到1万个以上,且包 ...
- java基础篇---文件上传(smartupload组件)
文件上传几乎是所有网站都具有的功能,用户可以将文件上传到服务器的指定文件夹中,也可以保存在数据库中,本篇主要说明smartupload组件上传. 在讲解smartupload上传前,我们先来看看不使用 ...
- java 大文件上传 断点续传 完整版实例 (Socket、IO流)
ava两台服务器之间,大文件上传(续传),采用了Socket通信机制以及JavaIO流两个技术点,具体思路如下: 实现思路: 1.服:利用ServerSocket搭建服务器,开启相应端口,进行长连接操 ...
- JAVA大文件上传断点续传解决方案
javaweb上传文件 上传文件的jsp中的部分 上传文件同样可以使用form表单向后端发请求,也可以使用 ajax向后端发请求 1.通过form表单向后端发送请求 <form id=" ...
- H5+JAVA的文件上传,断点续传
这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时候,向后端传入参数:当前为第几块文件,和分片总数 下面直接贴代码吧,一些难懂的我大部分都加上注释了: 上传文件实体类: 看得 ...
- js文件上传下载组件
在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现. 先说下要求: PC端全平台支持,要求支持Windows,Mac,Linux 支持所 ...
随机推荐
- Redis学习总结(1)——数据持久化
以前研究Redis的时候,很多东西都不太明白,理解得也不太深,现在有时间重新拾起来看看,将一些心得记录下来,希望和大家一起探讨. 一.简介 Redis是一个单线程高可用的Key-Value存储系统,和 ...
- Future和Promise
Future用于获取异步操作的结果,而Promise则比较抽象,无法直接猜测出其功能. Future Future最早来源于JDK的java.util.concurrent.Future,它用于代表异 ...
- 前馈网络求导概论(一)·Softmax篇
Softmax是啥? Hopfield网络的能量观点 1982年的Hopfiled网络首次将统计物理学的能量观点引入到神经网络中, 将神经网络的全局最小值求解,近似认为是求解热力学系统的能量最低点(最 ...
- 简述HTML DOM及其节点分类
在JavaScript中,document这个对象大家一定很熟悉,哪怕是刚刚开始学习的新人,也会很快接触到这个对象.而document对象不仅仅是一个普通的JavaScript内置对象,它还是一个巨大 ...
- haahah
#DB ``` import os basedir = os.path.abspath(os.path.dirname(__file__)) SQLALCHEMY_DATABASE_URI = ' ...
- javaFx:使用弹出对话框 Alert
javaFx8 自带的对话框非常好用,类似的使用方式如下: /** * 弹出一个通用的确定对话框 * @param p_header 对话框的信息标题 * @param p_message 对话框的信 ...
- SQL Server 2008 表变量 临时表
最近做一个报表,其中 在报表中用到了存储过程,游标,cte表达式,临时表和表变量. 在游标中循环遍历cte中的数据,把对应的数据存放在变量里面,之后把变量插入到表变量中,游标结束后,想要根据存储过程的 ...
- Ubuntu安装Oracle SQLDeveloper
1、下载Oracle安装文件 这里我下载的是Linux RPM版本,文件名为sqldeveloper-4.0.3.16.84-1.noarch.rpm http://www.oracle.com/te ...
- ajax和jquery
ajax的定义: AJAX即“Asynchronous Javascript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术. AJAX = 异步 Ja ...
- 前端自动化工具gulp自动添加版本号
之前,我介绍了学习安装并配置前端自动化工具Gulp,觉得gulp确实比grunt的配置简单很多,于是我决定再深入学习一下gulp,就去网上查了资料,发现gulp还可以自动添加版本号,这个功能就为我平时 ...