斷點上傳,java裏面比較靠譜一點的,通常都會選用Flex。我認可,Flex只是摸了一下,不精通。HTML 5 有個Blob對象(File對象繼承它),這個對象有個方法slice方法,能夠對一個文件進行分片。基於前些日子寫的《WebSocket的服務端實現》,再加上HTML 5的File API(可參考:HTML5中的文件處理 之 File API,或者W3C File API)和WebStorage。作了一個段斷點續傳的demo。javascript
代碼比較挫,把一個文件分紅一個個小片以後,每一次上傳都會將已經上傳的字節數放到LocalStorage裏面。關於LocalStorage裏面緩存怎麼清,能夠參看Chrome - HTML 5 本地存儲。若是上傳的途中暫停或者直接關掉瀏覽器。下次上傳的時候,就會從那個節點開始上傳。html
PS:demo操做,請先創建WS鏈接以後,再選擇文件上傳html5
代碼請笑納:java
<!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; } }
服務端:web
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(); } }