使用websocket上传文件的简单例子: 使用Html5的WebSocket在浏览器上传文件
上篇文章没有解决的问题就是大文件的上传问题, 而且多文件上传问题也未协调. 所以这篇文章就是解决这两个问题的.
如果将一个大文件直接读入内存再发送的话, 内存会吃不消, 所以我们把大文件分块传输. Html5的Fileread方法提供了读取文件部分内容Blob的方法.
为了保证后台接收到的分块数据的顺序不会乱掉, 我们需要后台确定写入分块数据后再发送下一块数据.
在Html端:
<!DOCTYPE html> <html> <head> <title>WebSocket Chat Client</title> <meta charset="utf-8" /> <script type="text/javascript" src="jquery-1.6.4.min.js"></script> <script type="text/javascript" src="jquery.json-2.3.min.js"></script> <script type="text/javascript"> $().ready( function() { // Check for the various File API support. if (window.File && window.FileReader && window.FileList && window.Blob) { // Great success! All the File APIs are supported. } else { alert('The File APIs are not fully supported in this browser.'); } }); //在消息框中打印内容 function log(text) { $("#log").append(text+"\n"); } //全局的websocket变量
var ws; var paragraph = 10485760; var fileList ; var file; var startSize,endSize = 0; var i = 0; j = 0; //连接服务器 $(function() { $("#connect").click(function() { ws = new WebSocket($("#uri").val()); //连接成功建立后响应 ws.onopen = function() { log("成功连接到" + $("#uri").val()); } //收到服务器消息后响应 ws.onmessage = function(e) { log("服务器说" + e.data + (e.data=="ok")); if(e.data == "ok"){ if(endSize < file.size){ startSize = endSize; endSize += paragraph ; if (file.webkitSlice) { var blob = file.webkitSlice(startSize, endSize); } else if (file.mozSlice) { var blob = file.mozSlice(startSize, endSize); } var reader = new FileReader(); reader.readAsArrayBuffer(blob); reader.onload = function loaded(evt) { var ArrayBuffer = evt.target.result; log("发送文件第" + (i++) + "部分"); ws.send(ArrayBuffer); } } else{ startSize = endSize = 0; i = 0; log("发送" + file.name +"完毕"); file = fileList[j++]; if(file.name){ ws.send(file.name); } log("发送文件完毕"); } } //连接关闭后响应 ws.onclose = function() { log("关闭连接"); ws = null; } return false; } }); }); $(function() { $("#sendFileForm").click(function() { fileList = document.getElementById("file").files; file = fileList[0]; ws.send(file.name); }) }); $(function() { $("#reset").click(function() { $("#log").empty(); return false; }); }); </script> </head> <body> <span>Html5功能测试</span> <span id="progress">0</span><br> <input type="text" id="uri" value="ws://localhost:8887" style="width: 200px;"> <input type="button" id="connect" value="Connect"><input type="button" id="disconnect" value="Disconnect" disabled="disabled"> <form > <input id="file" type="file" multiple /> <input type="button" id="sendFileForm" value="测试" /> <input type="button" id="reset" value="清空消息框" /> </form> <form> <textarea id="log" rows="30" cols="100" style="font-family: monospace; color: red;"></textarea> </form> </body> </html>
这里设置了文件大于paragraph (10M)时就会分块发送文件.
服务器端:
/** * 处理字符串消息 */ public void onMessage( WebSocket conn, String message ) { System.out.println("文件名" + message); //将文件名写入连接对象中,(需要手动修改webSocket类) conn.setFileName(message); try { conn.send("ok"); } catch (NotYetConnectedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
/** * 处理二进制消息 */ public void onMessage(WebSocket conn, byte[] message) { System.out.println("收到二进制流:"); //将二进制流保存为文件, 文件名从连接对象中取出 saveFileFromBytes(message, "src/" + conn.getFileName()); //告诉前台可以继续发送了. try { conn.send("ok"); } catch (NotYetConnectedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 将二进制byte[]数组写入文件中 * @param b byte[]数组 * @param outputFile 文件位置 * @return 成功: true 失败: false */ public static boolean saveFileFromBytes(byte[] b, String outputFile) { FileOutputStream fstream = null; File file = null; try { file = new File(outputFile); fstream = new FileOutputStream(file, true); fstream.write(b); } catch (Exception e) { e.printStackTrace(); return false; } finally { if (fstream != null) { try { fstream.close(); } catch (IOException e1) { e1.printStackTrace(); } } } return true; }
好了, 顺序发送保证了后台写入的数据也是顺序的, 文件就不会出错了! 搞定!