HTML 5 断点续上传
断点上传,java里面比较靠谱一点的,一般都会选用Flex。我承认,Flex只是摸了一下,不精通。HTML 5 有个Blob对象(File对象继承它),这个对象有个方法slice方法,可以对一个文件进行分片。基于前些日子写的《WebSocket的服务端实现》,再加上HTML 5的File API(可参考:HTML5中的文件处理 之 File API,或者W3C File API)和WebStorage。做了一个段断点续传的demo。
代码比较挫,把一个文件分成一个个小片之后,每一次上传都会将已经上传的字节数放到LocalStorage里面。关于LocalStorage里面缓存怎么清,可以参看Chrome - HTML 5 本地存储。如果上传的途中暂停或者直接关掉浏览器。下次上传的时候,就会从那个节点开始上传。
PS:demo操作,请先建立WS连接之后,再选择文件上传
代码请笑纳:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<! DOCTYPE html> < html > < head > < title >使用WebSocket实现断点续传文件</ title > < meta charset = "utf-8" > </ head > < script type = "text/javascript" src = "demo.js" ></ script > < body onload = "init();" > < button onclick = "webSocketConn();" >创建连接</ button >(step1) < div class = "row" > < label for = "fileToUpload" >Select a File to Upload</ label > < input type = "file" name = "fileToUpload" id = "fileToUpload" onchange = "fileSelected();" />(step2) </ div > < div id = "fileName" ></ div > < div id = "fileSize" ></ div > < div id = "fileType" ></ div > < div class = "row" > < button onclick = "sendFileName();uploadFile()" >上传</ button >(step3) < button onclick = "pauseUpload()" >暂停</ button > < label id = "progressNumber" ></ label > </ div > < div id = "msg" style = "max-height: 400px; overflow:auto;min-height: 100px;" > </ div > </ body > </ html > |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
####### 断点续传 ###### author:linrb createTime: 2012-08-22 QQ: 569830404 */ var websocket = null ; //websocket var msg = null ; //日志 var paragraph = 10240; //每次分片传输文件的大小 10KB var blob = null ; // 分片数据的载体Blob对象 var file = null ; //传输的文件 var startSize,endSize = 0; //分片的始终字节点 var uploadState = 0; // 0: 无上传/取消, 1: 上传中, 2: 暂停 //初始化消息框 function init(){ msg = document.getElementById( "msg" ); } /** * 分片上传文件 */ function uploadFile() { if (file){ //将上传状态设置成1 uploadState = 1; endSize = getLastestUploadEndSize(file); var reader = new FileReader(); reader.onload = function loaded(evt) { var ArrayBuffer = evt.target.result; websocket.send(ArrayBuffer); uploadProgress(endSize); }; if (endSize < file.size){ //先发送文件名称 //websocket.send(file.name); //处理文件发送(字节) startSize = endSize; if (paragraph > (file.size - endSize)){ endSize = file.size; } else { endSize += paragraph ; } if (file.webkitSlice) { //webkit浏览器 blob = file.webkitSlice(startSize, endSize); } else blob = file.slice(startSize, endSize); reader.readAsArrayBuffer(blob); } } } //显示处理进程 function uploadProgress(uploadLen) { var percentComplete = Math.round(uploadLen * 100 / file.size); document.getElementById( 'progressNumber' ).innerHTML = percentComplete.toString() + '%' ; //保存到LocalStorage一边下次传输,可以记忆起这个断点 localStorage.setItem(file.lastModifiedDate + "_" + file.name, uploadLen); } //WebSocket连接 function webSocketConn(){ try { var readyState = new Array( "正在连接" , "已建立连接" , "正在关闭连接" , "已关闭连接" ); var host = "ws://localhost:8000" ; websocket = new WebSocket(host); websocket.onopen = function (){ msg.innerHTML += "<p>Socket状态: " + readyState[websocket.readyState] + "</p>" ; }; websocket.onmessage = function (event){ //每上传一个分片之后,等待介绍了服务端的提示之后再做下一个分片上传 if (event.data.indexOf( "ok" ) != -1 && uploadState == 1){ if (endSize == file.size){ localStorage.removeItem(file.lastModifiedDate + "_" + file.name); msg.innerHTML += "<p>上传完成!!</p>" ; websocket.close(); //结束上传 } else { uploadFile(); } } }; websocket.onclose = function (){ msg.innerHTML += "<p>Socket状态: " + readyState[websocket.readyState] + "</p>" ; }; msg.innerHTML += "<p>Socket状态: " + readyState[websocket.readyState] + "</p>" ; } catch (exception){ msg.innerHTML += "<p>有错误发生</p>" ; return ; } } /* 暂停上传 */ function pauseUpload(){ uploadState = 2; } /** * 从localStorage检查最后一次上传的字节 */ function getLastestUploadEndSize(uploadFile){ var lastestLen = localStorage.getItem(uploadFile.lastModifiedDate + "_" + uploadFile.name); if (lastestLen){ return parseInt(lastestLen); } else { return 0; } } /* 发送文件名 */ function sendFileName(){ websocket.send(file.name); } /** * 选择文件之后触发事件 */ function fileSelected() { file = document.getElementById( 'fileToUpload' ).files[0]; if (file) { var fileSize = 0; if (file.size > 1024 * 1024) fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB' ; else fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB' ; document.getElementById( 'fileName' ).innerHTML = 'Name: ' + file.name; document.getElementById( 'fileSize' ).innerHTML = 'Size: ' + fileSize; document.getElementById( 'fileType' ).innerHTML = 'Type: ' + file.type; } } |
服务端:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
|
package fileUpload; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.security.MessageDigest; public class UploadServer { private int port = 8000 ; private ServerSocket serverSocket; public UploadServer() throws IOException { serverSocket = new ServerSocket(port); System.out.println( "服务器启动" ); } private void service() { Socket socket = null ; while ( true ) { try { socket = serverSocket.accept(); Thread workThread = new Thread( new Handler(socket)); workThread.start(); } catch (IOException e) { e.printStackTrace(); } } } class Handler implements Runnable { private Socket socket; private boolean hasHandshake = false ; Charset charset = Charset.forName( "UTF-8" ); private File file = null ; private FileOutputStream fileOut = null ; public Handler(Socket socket) { this .socket = socket; } private PrintWriter getWriter(Socket socket) throws IOException { OutputStream socketOut = socket.getOutputStream(); return new PrintWriter(socketOut, true ); } public String echo(String msg) { return "echo:" + msg; } public void run() { try { System.out.println( "New connection accepted" + socket.getInetAddress() + ":" + socket.getPort()); InputStream in = socket.getInputStream(); PrintWriter pw = getWriter(socket); //读入缓存 byte [] buf = new byte [ 1024 ]; //读到字节 int len = in.read(buf, 0 , 1024 ); //读到字节数组 byte [] res = new byte [len]; System.arraycopy(buf, 0 , res, 0 , len); String key = new String(res); if (!hasHandshake && key.indexOf( "Key" ) > 0 ){ key = key.substring( 0 , key.indexOf( "==" ) + 2 ); key = key.substring(key.indexOf( "Key" ) + 4 , key.length()).trim(); key+= "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ; MessageDigest md = MessageDigest.getInstance( "SHA-1" ); md.update(key.getBytes( "utf-8" ), 0 , key.length()); byte [] sha1Hash = md.digest(); sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder(); key = encoder.encode(sha1Hash); pw.println( "HTTP/1.1 101 Switching Protocols" ); pw.println( "Upgrade: websocket" ); pw.println( "Connection: Upgrade" ); pw.println( "Sec-WebSocket-Accept: " + key); pw.println(); pw.flush(); hasHandshake = true ; //接收数据 byte [] first = new byte [ 1 ]; int read = in.read(first, 0 , 1 ); while (read > 0 ){ int b = first[ 0 ] & 0xFF ; //boolean fin = (b & 0x80) > 0; // int rsv = (b & 0x70) >>> 4; byte opCode = ( byte ) (b & 0x0F ); if (opCode == 8 ){ socket.getOutputStream().close(); file = null ; fileOut.flush(); fileOut.close(); fileOut = null ; break ; } b = in.read(); int payloadLength = b & 0x7F ; if (payloadLength == 126 ) { byte [] extended = new byte [ 2 ]; in.read(extended, 0 , 2 ); int shift = 0 ; payloadLength = 0 ; for ( int i = extended.length - 1 ; i >= 0 ; i--) { payloadLength = payloadLength + ((extended[i] & 0xFF ) << shift); shift += 8 ; } } else if (payloadLength == 127 ) { byte [] extended = new byte [ 8 ]; in.read(extended, 0 , 8 ); int shift = 0 ; payloadLength = 0 ; for ( int i = extended.length - 1 ; i >= 0 ; i--) { payloadLength = payloadLength + ((extended[i] & 0xFF ) << shift); shift += 8 ; } } //掩码 byte [] mask = new byte [ 4 ]; in.read(mask, 0 , 4 ); int readThisFragment = 1 ; ByteBuffer byteBuf = ByteBuffer.allocate(payloadLength); while (payloadLength > 0 ){ int masked = in.read(); masked = masked ^ (mask[( int ) ((readThisFragment - 1 ) % 4 )] & 0xFF ); byteBuf.put(( byte ) masked); payloadLength--; readThisFragment++; } byteBuf.flip(); if (opCode == 1 ){ getChar(byteBuf.array()); } else outFile(byteBuf.array()); in.read(first, 0 , 1 ); } } in.close(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (socket != null ) socket.close(); } catch (IOException e) { e.printStackTrace(); } } } private void responseClient( boolean finalFragment) throws IOException { ByteBuffer byteBuf = ByteBuffer.allocate( 10 ); byteBuf.put( "ok" .getBytes( "UTF-8" )); OutputStream out = socket.getOutputStream(); int first = 0x00 ; //是否是输出最后的WebSocket响应片段,默认 if (finalFragment) { first = first + 0x80 ; first = first + 0x1 ; } out.write(first); if (byteBuf.limit() < 126 ) { out.write(byteBuf.limit()); } else if (byteBuf.limit() < 65536 ) { out.write( 126 ); out.write(byteBuf.limit() >>> 8 ); out.write(byteBuf.limit() & 0xFF ); } else { // Will never be more than 2^31-1 out.write( 127 ); out.write( 0 ); out.write( 0 ); out.write( 0 ); out.write( 0 ); out.write(byteBuf.limit() >>> 24 ); out.write(byteBuf.limit() >>> 16 ); out.write(byteBuf.limit() >>> 8 ); out.write(byteBuf.limit() & 0xFF ); } // Write the content out.write(byteBuf.array(), 0 , byteBuf.limit()); out.flush(); } /** * 方法说明: * @开发:linrb * @创建时间:2012-8-21 * @param array * @throws IOException */ private void getChar( byte [] array) throws IOException { ByteArrayInputStream byteIn = new ByteArrayInputStream(array); InputStreamReader reader = new InputStreamReader(byteIn, charset.newDecoder()); int b = 0 ; String res = "" ; try { while ((b = reader.read()) > 0 ){ res += ( char )b; } } catch (IOException e) { e.printStackTrace(); } file = new File( "C:/" + res); } /** * 方法说明: * @开发:linrb * @创建时间:2012-8-14 * @param array * @throws IOException */ private void outFile( byte [] array) throws IOException { if (fileOut == null ){ fileOut = new FileOutputStream(file, true ); } fileOut.write(array); responseClient( true ); } } public static void main(String[] args) throws IOException { new UploadServer().service(); } } |
HTML 5 断点续上传的更多相关文章
- iOS通过切片仿断点机制上传文件
项目开发中,有时候我们需要将本地的文件上传到服务器,简单的几张图片还好,但是针对iPhone里面的视频文件进行上传,为了用户体验,我们有必要实现断点上传.其实也不是真的断点,这里我们只是模仿断点机制. ...
- C#使用七牛云存储上传下载文件、自定义回调
项目需要将音视频文件上传服务器,考虑并发要求高,通过七牛来实现. 做了一个简易的压力测试,同时上传多个文件,七牛自己应该有队列处理并发请求,我无论同时提交多少个文件,七牛是批量一个个排队处理了. 一个 ...
- Linux下七牛云存储qrsync命令行上传同步工具
原址:https://m.aliyun.com/yunqi/ziliao/54370 VPS数据备份是一个重要的工作,之前在文章:使用七牛云存储自动备份VPS数据分享过使用七牛云存储提供的工具QRSB ...
- Android实现TCP断点上传,后台C#服务实现接收
终端实现大文件上传一直都是比较难的技术,其中涉及到后端与前端的交互,稳定性和流量大小,而且实现原理每个人都有自己的想法,后端主流用的比较多的是Http来实现,因为大多实现过断点下载.但稳定性不能保证, ...
- 实现TCP断点上传,后台C#服务实现接收
实现TCP断点上传,后台C#服务实现接收 终端实现大文件上传一直都是比较难的技术,其中涉及到后端与前端的交互,稳定性和流量大小,而且实现原理每个人都有自己的想法,后端主流用的比较多的是Http来实现, ...
- 文件断点上传,html5实现前端,java实现服务器
断点上传能够防止意外情况导致上传一半的文件下次上传时还要从头下载,网上有很多关于断点的实现,这篇文章只是从前到后完整的记录下一个可用的实例,由于生产环境要求不高,而且就是提供给一两个人用,所以我简化了 ...
- 【翻译】tus----一个可续传文件上传的开放协议
tus tus是一个可续穿文件上传协议,它以Http协议为载体,统一了一个文件断点续传的标准. 这篇文章翻译自https://tus.io/ 目前该协议版本信息如下: Version: 1.0.0 ( ...
- Spring Boot 2.x(十六):玩转vue文件上传
为什么使用Vue-Simple-Uploader 最近用到了Vue + Spring Boot来完成文件上传的操作,踩了一些坑,对比了一些Vue的组件,发现了一个很好用的组件--Vue-Simple- ...
- Android客户端实现七牛云存储文件上传
1.简单文件上传 上传模型如下. 1.1获得Token 不管是简单文件上传,还是分片上传.断点续传 都需要首先访问服务器,以获得上传凭证信息Token..用于测试时,可以用本地模拟Token信息(有 ...
随机推荐
- zabbix安装及简单配置
Zabbix基本介绍: zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案.它能监视各种网络参数,保证服务器系统的安全运营:并提供柔软的通知机制以让系统管理员快 ...
- Java 企业级 JavaEE
授权协议:CDDL 开发语言:Java 操作系统:跨平台 开发厂商:Oracle 原文:https://www.oschina.net/p/j2ee Java EE 详细介绍 这是SUN公司推出的J2 ...
- 来自工程师的8项Web性能提升建议
在互联网盛行的今天,越来越多的在线用户希望得到安全可靠并且快速的访问体验.针对Web网页过于膨胀以及第三脚本蚕食流量等问题,Radware向网站运营人员提出以下改进建议,帮助他们为用户提供最快最优质的 ...
- wpf Assembly.LoadFile dll GetType 反射 抛异常 不具有由 URI 识别的资源。
public static void LoadViewFromUri(this Window window, string baseUri) { try { var resourceLocater = ...
- cf792b循环链表
头尾链接一下就好, /* 1 2 3 4 5 6 7:4 5 6 7 1 2 3:2 3 5 6 7 1:5 6 7 1 3:6 7 1 3:1 3 7 */ #include<bits/std ...
- python接口自动化测试二十:函数写接口测试
# coding:utf-8import requestsimport refrom bs4 import BeautifulSoup # s = requests.session() # 全局的s ...
- js中字符串概念
字符串概念:所有带单引号和双引号的叫做字符串 字符串的数据类型:字符串既是基本数据类型,又是复合数据类型. 字符串存储在内存里[只读数据段]的地方.字符串的变量里存储的是字符串的地址. [注]使用起来 ...
- VS-常用的快捷键-总结
1: 快速添加引用 === Shift+Alt+F10; 也可用于实现抽象类 2: 直接添加属性 ===prop+Tab+Tab; 3: 根据字段添加属性 === Ctrl +r+e; 4: 格式化代 ...
- 遍历集合的Iterator删除其中的元素
package list; import java.util.LinkedList; /* * 遍历集合的时候删除其中的元素 从后往前删,每次都删除的是最后一个元素,不涉及移位 */public cl ...
- visual studio 2015百度云下载
visual studio 2015百度云下载 https://pan.baidu.com/s/1b198Zo3mX5_zA2VX3xRRfw 提取码: 关注公众号[GitHubCN]回复2015获取