断点上传,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();
}
}
|