HTML 5 斷點續上傳

         斷點上傳,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>
	&nbsp;<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();
	}
}
相關文章
相關標籤/搜索