断点上传,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连接之后,再选择文件上传
代码请笑纳:
<!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>
####### 断点续传 ######
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;
}
}
服务端:
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();
}
}