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信息(有 ...
随机推荐
- [Java]求文件大小并保留两位小数(文件大小是一个长整型数单位是Byte)
前言 为了获得一堆apk的大小,并与人类友好方式显示.本来是打算用以下方法,到时不能具体到保留两位小数. org.apache.commons.io.FileUtils.byteCountToDisp ...
- STM32F412应用开发笔记之十:多组分气体分析仪设计验证
本次将NUCLEO-F412ZG应用于我们的多组分气体分析仪的实现试验,从整体上测试实际项目的应用情况. 一.项目概述 多组分气体分析仪是我公司近期研发的三个主要产品之一.采用模块化设计,可增减配置, ...
- SpringMVC(3):DispatcherServlet详解
原文出处: 张开涛 3.1.DispatcherServlet作用 DispatcherServlet是前端控制器设计模式的实现,提供spring Web MVC的集中访问点,而且负责职责的分派,而且 ...
- LeetCode(56):合并区间
Medium! 题目描述: 给出一个区间的集合,请合并所有重叠的区间. 示例 1: 输入: [[1,3],[2,6],[8,10],[15,18]] 输出: [[1,6],[8,10],[15,18] ...
- PHP抽象类
<?php /* * abstract * 抽象类: * 1.至少有一个抽象方法(没有具体实现的方法) * 2.不能被实例化,可以被继承 * 3.抽象类可以有子抽象类 * 相对于接口: * 1. ...
- 区间dp的一些模式和总结
参考博客:https://blog.csdn.net/my_sunshine26/article/details/77141398 https://blog.csdn.net/qq_38569113/ ...
- bzoj 1064 noi2008 假面舞会题解
莫名其妙的变成了我们的noip互测题... 其实这题思想还是比较简单的,只是分类不好分而已 其实就是一个dfs的事 首先,非常明显,原题目中的所有关系可以抽象成一个图(这是...显而易见的吧...) ...
- python 全栈开发,Day64(视图,触发器,函数,存储过程,事务)
昨日内容回顾 pymysql:属于python的一个模块 pip3 install pymysql conn = pymysql.connect(...,charset = 'uft8') 创建游标 ...
- hdu 4707 仓鼠 记录深度 (BFS)
题意:linji的仓鼠丢了,他要找回仓鼠,他在房间0放了一块奶酪,按照抓鼠手册所说,这块奶酪可以吸引距离它D的仓鼠,但是仓鼠还是没有出现,现在给出一张关系图,表示各个房间的关系,相邻房间距离为1,而且 ...
- Linux使用netstat命令查看并发连接数
我们的网站部署在linux的服务器上,特别是web服务器,我们可能有时候做为运维人员,肯定是要查看网站的并发连接数是不是达到瓶颈等,所以在linux下,我们如何查看服务器的并发连接数呢?使用以下命令即 ...