@OnMessage public void savePart(byte[] part, Session ses) { if (uploadFile == null) { if (fileName != null) try { uploadFile = new RandomAccessFile(fileName, "rw"); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); return; } } if (uploadFile != null) try { uploadFile.write(part); System.err.printf("Stored part of %db%n", part.length); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
此外还可以为OnClose事件加入一个处理函数,万一出现连接异常关闭的情况,它将负责删除不完整的文件。
客户端的实现利用了HTML5中的工作线程(Worker)功能,不幸的是,Firefox没有采用在Worker中实现文件对象克隆的方式,因此这个示例只能在IE或Chrome中进行测试。如果该解决方案对于浏览器的可移植性有很高的要求,那么可以用一个不使用Worker的 JavaScript代码段来代替这个基于Worker的解决方案。但由于未使用独立的线程(即Worker),因此这种方案的性能会有所下降。 Worker的代码如下所示:
var files = []; var endPoint = "ws" + (self.location.protocol == "https:" ? "s" : "") + "://" + self.location.hostname + (self.location.port ? ":" + self.location.port : "") + "/echoserver/upload/*"; var socket; var ready; function upload(blobOrFile) { if (ready) socket.send(blobOrFile); } function openSocket() { socket = new WebSocket(endPoint); socket.onmessage = function(event) { self.postMessage(JSON.parse(event.data)); }; socket.onclose = function(event) { ready = false; }; socket.onopen = function() { ready = true; process(); }; } function process() { while (files.length > 0) { var blob = files.shift(); socket.send(JSON.stringify({ "cmd" : 1, "data" : blob.name })); const BYTES_PER_CHUNK = 1024 * 1024 * 2; // 1MB chunk sizes. const SIZE = blob.size; var start = 0; var end = BYTES_PER_CHUNK; while (start < SIZE) { if ('mozSlice' in blob) { var chunk = blob.mozSlice(start, end); } blob) { var chunk = blob.slice(start, end); } else { var chunk = blob.webkitSlice(start, end); } upload(chunk); start = end; end = start + BYTES_PER_CHUNK; } socket.send(JSON.stringify({ "cmd" : 2, "data" : blob.name })); //self.postMessage(blob.name + " Uploaded Succesfully"); } } self.onmessage = function(e) { for (var j = 0; j < e.data.files.length; j++) files.push(e.data.files[j]); //self.postMessage("Job size: "+files.length); if (ready) { process(); } else openSocket(); }
很方便的一点在于,与Worker进行交互的JavaScript代码也能够利用消息传递机制。当用户在浏览器中选择文件进行上传时,这一操作的信息就会传递给Worker。后者会以批量的方式处理第一个准备上传的文件,它将文件分成多个片段,即多个块,然后通过WebSocket将这些块依次上传。最后发送一个cmd = 2的命令消息。而命令消息的处理函数会将消息重新发送给主JavaScript代码,通知所上传的文件已经完成了。如果客户端选择上传许多大文件,那么这段代码会对浏览器端带来相当大的压力。为此需要对代码进行重新调整,让它在收到上一个文件上传成功的消息后才继续上传下一个文件。这部分内容的修改就留给各位读者作为一个练习吧。在附录1中可以找到本示例的完整源代码。
实现 Websocket 的浏览器Chrome Supported in version 4+
Firefox Supported in version 4+
Internet Explorer Supported in version 10+
Opera Supported in version 10+
Safari Supported in version 5+
实现 Websocket 协议服务器端项目jetty 7.0.1 包含了一个初步的实现
resin
pywebsocket apache http server 扩展
apache tomcat 7.0.27 版本
Nginx 1.3.13 版本
jWebSocket java实现版
参考资料Websocket Demo