├── package.json
├── README.md
├── server.js
├── index.html
└── public
└── SkyRTC-client.js
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "SkyRTC-demo",
3 | "description": "A simple SkyRTC demo",
4 | "dependencies": {
5 | "ws": ">= 0.0.0",
6 | "node-uuid": ">= 1.4.1",
7 | "express": "3.1.0",
8 | "skyrtc": ">= 0.0.0"
9 | },
10 | "readmeFilename": "README.md"
11 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #一个简单的SkyRTC示例
2 | ---
3 | ##简介
4 | 这是一个使用SkyRTC和SkyRTC-client搭建浏览器中音频、视频、文字聊天室的Demo
5 |
6 | ##安装和使用
7 | 1. 安装Node.js及npm环境
8 | 2. 下载源码到本地,并解压缩
9 | 3. 移动到解压后的目录下
10 | 4. 使用命令`npm install`安装所需要的库
11 | 5. 运行命令`node server.js`,建议配合`forever`
12 | 6. 访问`localhost:3000#roomName`查看效果,其中`roomName`为进入的房间名,不同房间的用户无法互相通信
13 |
14 | ##功能说明
15 | 支持划分房间的在线音频、视频、文字聊天,提供房间内文件共享功能
16 |
17 | ##SkyRTC项目链接
18 | [SkyRTC项目](https://github.com/LingyuCoder/SkyRTC)
19 |
20 | [SkyRTC-client项目](https://github.com/LingyuCoder/SkyRTC-client)
21 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var app = express();
3 | var server = require('http').createServer(app);
4 | var SkyRTC = require('skyrtc').listen(server);
5 | var path = require("path");
6 |
7 | var port = process.env.PORT || 3000;
8 | server.listen(port);
9 |
10 | app.use(express.static(path.join(__dirname, 'public')));
11 |
12 | app.get('/', function(req, res) {
13 | res.sendfile(__dirname + '/index.html');
14 | });
15 |
16 | SkyRTC.rtc.on('new_connect', function(socket) {
17 | console.log('创建新连接');
18 | });
19 |
20 | SkyRTC.rtc.on('remove_peer', function(socketId) {
21 | console.log(socketId + "用户离开");
22 | });
23 |
24 | SkyRTC.rtc.on('new_peer', function(socket, room) {
25 | console.log("新用户" + socket.id + "加入房间" + room);
26 | });
27 |
28 | SkyRTC.rtc.on('socket_message', function(socket, msg) {
29 | console.log("接收到来自" + socket.id + "的新消息:" + msg);
30 | });
31 |
32 | SkyRTC.rtc.on('ice_candidate', function(socket, ice_candidate) {
33 | console.log("接收到来自" + socket.id + "的ICE Candidate");
34 | });
35 |
36 | SkyRTC.rtc.on('offer', function(socket, offer) {
37 | console.log("接收到来自" + socket.id + "的Offer");
38 | });
39 |
40 | SkyRTC.rtc.on('answer', function(socket, answer) {
41 | console.log("接收到来自" + socket.id + "的Answer");
42 | });
43 |
44 | SkyRTC.rtc.on('error', function(error) {
45 | console.log("发生错误:" + error.message);
46 | });
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SkyRTC聊天室Demo
6 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
219 |
220 |
--------------------------------------------------------------------------------
/public/SkyRTC-client.js:
--------------------------------------------------------------------------------
1 | var SkyRTC = function() {
2 | var PeerConnection = (window.PeerConnection || window.webkitPeerConnection00 || window.webkitRTCPeerConnection || window.mozRTCPeerConnection);
3 | var URL = (window.URL || window.webkitURL || window.msURL || window.oURL);
4 | var getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);
5 | var nativeRTCIceCandidate = (window.mozRTCIceCandidate || window.RTCIceCandidate);
6 | var nativeRTCSessionDescription = (window.mozRTCSessionDescription || window.RTCSessionDescription); // order is very important: "RTCSessionDescription" defined in Nighly but useless
7 | var moz = !!navigator.mozGetUserMedia;
8 | var iceServer = {
9 | "iceServers": [{
10 | "url": "stun:stun.l.google.com:19302"
11 | }]
12 | };
13 | var packetSize = 1000;
14 |
15 | /**********************************************************/
16 | /* */
17 | /* 事件处理器 */
18 | /* */
19 | /**********************************************************/
20 | function EventEmitter() {
21 | this.events = {};
22 | }
23 | //绑定事件函数
24 | EventEmitter.prototype.on = function(eventName, callback) {
25 | this.events[eventName] = this.events[eventName] || [];
26 | this.events[eventName].push(callback);
27 | };
28 | //触发事件函数
29 | EventEmitter.prototype.emit = function(eventName, _) {
30 | var events = this.events[eventName],
31 | args = Array.prototype.slice.call(arguments, 1),
32 | i, m;
33 |
34 | if (!events) {
35 | return;
36 | }
37 | for (i = 0, m = events.length; i < m; i++) {
38 | events[i].apply(null, args);
39 | }
40 | };
41 |
42 |
43 | /**********************************************************/
44 | /* */
45 | /* 流及信道建立部分 */
46 | /* */
47 | /**********************************************************/
48 |
49 |
50 | /*******************基础部分*********************/
51 | function skyrtc() {
52 | //本地media stream
53 | this.localMediaStream = null;
54 | //所在房间
55 | this.room = "";
56 | //接收文件时用于暂存接收文件
57 | this.fileData = {};
58 | //本地WebSocket连接
59 | this.socket = null;
60 | //本地socket的id,由后台服务器创建
61 | this.me = null;
62 | //保存所有与本地相连的peer connection, 键为socket id,值为PeerConnection类型
63 | this.peerConnections = {};
64 | //保存所有与本地连接的socket的id
65 | this.connections = [];
66 | //初始时需要构建链接的数目
67 | this.numStreams = 0;
68 | //初始时已经连接的数目
69 | this.initializedStreams = 0;
70 | //保存所有的data channel,键为socket id,值通过PeerConnection实例的createChannel创建
71 | this.dataChannels = {};
72 | //保存所有发文件的data channel及其发文件状态
73 | this.fileChannels = {};
74 | //保存所有接受到的文件
75 | this.receiveFiles = {};
76 | }
77 | //继承自事件处理器,提供绑定事件和触发事件的功能
78 | skyrtc.prototype = new EventEmitter();
79 |
80 |
81 | /*************************服务器连接部分***************************/
82 |
83 |
84 | //本地连接信道,信道为websocket
85 | skyrtc.prototype.connect = function(server, room) {
86 | var socket,
87 | that = this;
88 | room = room || "";
89 | socket = this.socket = new WebSocket(server);
90 | socket.onopen = function() {
91 | socket.send(JSON.stringify({
92 | "eventName": "__join",
93 | "data": {
94 | "room": room
95 | }
96 | }));
97 | that.emit("socket_opened", socket);
98 | };
99 |
100 | socket.onmessage = function(message) {
101 | var json = JSON.parse(message.data);
102 | if (json.eventName) {
103 | that.emit(json.eventName, json.data);
104 | } else {
105 | that.emit("socket_receive_message", socket, json);
106 | }
107 | };
108 |
109 | socket.onerror = function(error) {
110 | that.emit("socket_error", error, socket);
111 | };
112 |
113 | socket.onclose = function(data) {
114 | that.localMediaStream.close();
115 | var pcs = that.peerConnections;
116 | for (i = pcs.length; i--;) {
117 | that.closePeerConnection(pcs[i]);
118 | }
119 | that.peerConnections = [];
120 | that.dataChannels = {};
121 | that.fileChannels = {};
122 | that.connections = [];
123 | that.fileData = {};
124 | that.emit('socket_closed', socket);
125 | };
126 |
127 | this.on('_peers', function(data) {
128 | //获取所有服务器上的
129 | that.connections = data.connections;
130 | that.me = data.you;
131 | that.emit("get_peers", that.connections);
132 | that.emit('connected', socket);
133 | });
134 |
135 | this.on("_ice_candidate", function(data) {
136 | var candidate = new nativeRTCIceCandidate(data);
137 | var pc = that.peerConnections[data.socketId];
138 | pc.addIceCandidate(candidate);
139 | that.emit('get_ice_candidate', candidate);
140 | });
141 |
142 | this.on('_new_peer', function(data) {
143 | that.connections.push(data.socketId);
144 | var pc = that.createPeerConnection(data.socketId),
145 | i, m;
146 | pc.addStream(that.localMediaStream);
147 | that.emit('new_peer', data.socketId);
148 | });
149 |
150 | this.on('_remove_peer', function(data) {
151 | var sendId;
152 | that.closePeerConnection(that.peerConnections[data.socketId]);
153 | delete that.peerConnections[data.socketId];
154 | delete that.dataChannels[data.socketId];
155 | for (sendId in that.fileChannels[data.socketId]) {
156 | that.emit("send_file_error", new Error("Connection has been closed"), data.socketId, sendId, that.fileChannels[data.socketId][sendId].file);
157 | }
158 | delete that.fileChannels[data.socketId];
159 | that.emit("remove_peer", data.socketId);
160 | });
161 |
162 | this.on('_offer', function(data) {
163 | that.receiveOffer(data.socketId, data.sdp);
164 | that.emit("get_offer", data);
165 | });
166 |
167 | this.on('_answer', function(data) {
168 | that.receiveAnswer(data.socketId, data.sdp);
169 | that.emit('get_answer', data);
170 | });
171 |
172 | this.on('send_file_error', function(error, socketId, sendId, file) {
173 | that.cleanSendFile(sendId, socketId);
174 | });
175 |
176 | this.on('receive_file_error', function(error, sendId) {
177 | that.cleanReceiveFile(sendId);
178 | });
179 |
180 | this.on('ready', function() {
181 | that.createPeerConnections();
182 | that.addStreams();
183 | that.addDataChannels();
184 | that.sendOffers();
185 | });
186 | };
187 |
188 |
189 | /*************************流处理部分*******************************/
190 |
191 |
192 | //创建本地流
193 | skyrtc.prototype.createStream = function(options) {
194 | var that = this;
195 |
196 | options.video = !!options.video;
197 | options.audio = !!options.audio;
198 |
199 | if (getUserMedia) {
200 | this.numStreams++;
201 | getUserMedia.call(navigator, options, function(stream) {
202 | that.localMediaStream = stream;
203 | that.initializedStreams++;
204 | that.emit("stream_created", stream);
205 | if (that.initializedStreams === that.numStreams) {
206 | that.emit("ready");
207 | }
208 | },
209 | function(error) {
210 | that.emit("stream_create_error", error);
211 | });
212 | } else {
213 | that.emit("stream_create_error", new Error('WebRTC is not yet supported in this browser.'));
214 | }
215 | };
216 |
217 | //将本地流添加到所有的PeerConnection实例中
218 | skyrtc.prototype.addStreams = function() {
219 | var i, m,
220 | stream,
221 | connection;
222 | for (connection in this.peerConnections) {
223 | this.peerConnections[connection].addStream(this.localMediaStream);
224 | }
225 | };
226 |
227 | //将流绑定到video标签上用于输出
228 | skyrtc.prototype.attachStream = function(stream, domId) {
229 | var element = document.getElementById(domId);
230 | if (navigator.mozGetUserMedia) {
231 | element.mozSrcObject = stream;
232 | element.play();
233 | } else {
234 | element.src = webkitURL.createObjectURL(stream);
235 | }
236 | element.src = webkitURL.createObjectURL(stream);
237 | };
238 |
239 |
240 | /***********************信令交换部分*******************************/
241 |
242 |
243 | //向所有PeerConnection发送Offer类型信令
244 | skyrtc.prototype.sendOffers = function() {
245 | var i, m,
246 | pc,
247 | that = this,
248 | pcCreateOfferCbGen = function(pc, socketId) {
249 | return function(session_desc) {
250 | pc.setLocalDescription(session_desc);
251 | that.socket.send(JSON.stringify({
252 | "eventName": "__offer",
253 | "data": {
254 | "sdp": session_desc,
255 | "socketId": socketId
256 | }
257 | }));
258 | };
259 | },
260 | pcCreateOfferErrorCb = function(error) {
261 | console.log(error);
262 | };
263 | for (i = 0, m = this.connections.length; i < m; i++) {
264 | pc = this.peerConnections[this.connections[i]];
265 | pc.createOffer(pcCreateOfferCbGen(pc, this.connections[i]), pcCreateOfferErrorCb);
266 | }
267 | };
268 |
269 | //接收到Offer类型信令后作为回应返回answer类型信令
270 | skyrtc.prototype.receiveOffer = function(socketId, sdp) {
271 | var pc = this.peerConnections[socketId];
272 | this.sendAnswer(socketId, sdp);
273 | };
274 |
275 | //发送answer类型信令
276 | skyrtc.prototype.sendAnswer = function(socketId, sdp) {
277 | var pc = this.peerConnections[socketId];
278 | var that = this;
279 | pc.setRemoteDescription(new nativeRTCSessionDescription(sdp));
280 | pc.createAnswer(function(session_desc) {
281 | pc.setLocalDescription(session_desc);
282 | that.socket.send(JSON.stringify({
283 | "eventName": "__answer",
284 | "data": {
285 | "socketId": socketId,
286 | "sdp": session_desc
287 | }
288 | }));
289 | }, function(error) {
290 | console.log(error);
291 | });
292 | };
293 |
294 | //接收到answer类型信令后将对方的session描述写入PeerConnection中
295 | skyrtc.prototype.receiveAnswer = function(socketId, sdp) {
296 | var pc = this.peerConnections[socketId];
297 | pc.setRemoteDescription(new nativeRTCSessionDescription(sdp));
298 | };
299 |
300 |
301 | /***********************点对点连接部分*****************************/
302 |
303 |
304 | //创建与其他用户连接的PeerConnections
305 | skyrtc.prototype.createPeerConnections = function() {
306 | var i, m;
307 | for (i = 0, m = this.connections.length; i < m; i++) {
308 | this.createPeerConnection(this.connections[i]);
309 | }
310 | };
311 |
312 | //创建单个PeerConnection
313 | skyrtc.prototype.createPeerConnection = function(socketId) {
314 | var that = this;
315 | var pc = new PeerConnection(iceServer);
316 | this.peerConnections[socketId] = pc;
317 | pc.onicecandidate = function(evt) {
318 | if (evt.candidate)
319 | that.socket.send(JSON.stringify({
320 | "eventName": "__ice_candidate",
321 | "data": {
322 | "label": evt.candidate.sdpMLineIndex,
323 | "candidate": evt.candidate.candidate,
324 | "socketId": socketId
325 | }
326 | }));
327 | that.emit("pc_get_ice_candidate", evt.candidate, socketId, pc);
328 | };
329 |
330 | pc.onopen = function() {
331 | that.emit("pc_opened", socketId, pc);
332 | };
333 |
334 | pc.onaddstream = function(evt) {
335 | that.emit('pc_add_stream', evt.stream, socketId, pc);
336 | };
337 |
338 | pc.ondatachannel = function(evt) {
339 | that.addDataChannel(socketId, evt.channel);
340 | that.emit('pc_add_data_channel', evt.channel, socketId, pc);
341 | };
342 | return pc;
343 | };
344 |
345 | //关闭PeerConnection连接
346 | skyrtc.prototype.closePeerConnection = function(pc) {
347 | if (!pc) return;
348 | pc.close();
349 | };
350 |
351 |
352 | /***********************数据通道连接部分*****************************/
353 |
354 |
355 | //消息广播
356 | skyrtc.prototype.broadcast = function(message) {
357 | var socketId;
358 | for (socketId in this.dataChannels) {
359 | this.sendMessage(message, socketId);
360 | }
361 | };
362 |
363 | //发送消息方法
364 | skyrtc.prototype.sendMessage = function(message, socketId) {
365 | if (this.dataChannels[socketId].readyState.toLowerCase() === 'open') {
366 | this.dataChannels[socketId].send(JSON.stringify({
367 | type: "__msg",
368 | data: message
369 | }));
370 | }
371 | };
372 |
373 | //对所有的PeerConnections创建Data channel
374 | skyrtc.prototype.addDataChannels = function() {
375 | var connection;
376 | for (connection in this.peerConnections) {
377 | this.createDataChannel(connection);
378 | }
379 | };
380 |
381 | //对某一个PeerConnection创建Data channel
382 | skyrtc.prototype.createDataChannel = function(socketId, label) {
383 | var pc, key, channel;
384 | pc = this.peerConnections[socketId];
385 |
386 | if (!socketId) {
387 | this.emit("data_channel_create_error", socketId, new Error("attempt to create data channel without socket id"));
388 | }
389 |
390 | if (!(pc instanceof PeerConnection)) {
391 | this.emit("data_channel_create_error", socketId, new Error("attempt to create data channel without peerConnection"));
392 | }
393 | try {
394 | channel = pc.createDataChannel(label);
395 | } catch (error) {
396 | this.emit("data_channel_create_error", socketId, error);
397 | }
398 |
399 | return this.addDataChannel(socketId, channel);
400 | };
401 |
402 | //为Data channel绑定相应的事件回调函数
403 | skyrtc.prototype.addDataChannel = function(socketId, channel) {
404 | var that = this;
405 | channel.onopen = function() {
406 | that.emit('data_channel_opened', channel, socketId);
407 | };
408 |
409 | channel.onclose = function(event) {
410 | delete that.dataChannels[socketId];
411 | that.emit('data_channel_closed', channel, socketId);
412 | };
413 |
414 | channel.onmessage = function(message) {
415 | var json;
416 | json = JSON.parse(message.data);
417 | if (json.type === '__file') {
418 | /*that.receiveFileChunk(json);*/
419 | that.parseFilePacket(json, socketId);
420 | } else {
421 | that.emit('data_channel_message', channel, socketId, json.data);
422 | }
423 | };
424 |
425 | channel.onerror = function(err) {
426 | that.emit('data_channel_error', channel, socketId, err);
427 | };
428 |
429 | this.dataChannels[socketId] = channel;
430 | return channel;
431 | };
432 |
433 |
434 |
435 | /**********************************************************/
436 | /* */
437 | /* 文件传输 */
438 | /* */
439 | /**********************************************************/
440 |
441 | /************************公有部分************************/
442 |
443 | //解析Data channel上的文件类型包,来确定信令类型
444 | skyrtc.prototype.parseFilePacket = function(json, socketId) {
445 | var signal = json.signal,
446 | that = this;
447 | if (signal === 'ask') {
448 | that.receiveFileAsk(json.sendId, json.name, json.size, socketId);
449 | } else if (signal === 'accept') {
450 | that.receiveFileAccept(json.sendId, socketId);
451 | } else if (signal === 'refuse') {
452 | that.receiveFileRefuse(json.sendId, socketId);
453 | } else if (signal === 'chunk') {
454 | that.receiveFileChunk(json.data, json.sendId, socketId, json.last, json.percent);
455 | } else if (signal === 'close') {
456 | //TODO
457 | }
458 | };
459 |
460 | /***********************发送者部分***********************/
461 |
462 |
463 | //通过Dtata channel向房间内所有其他用户广播文件
464 | skyrtc.prototype.shareFile = function(dom) {
465 | var socketId,
466 | that = this;
467 | for (socketId in that.dataChannels) {
468 | that.sendFile(dom, socketId);
469 | }
470 | };
471 |
472 | //向某一单个用户发送文件
473 | skyrtc.prototype.sendFile = function(dom, socketId) {
474 | var that = this,
475 | file,
476 | reader,
477 | fileToSend,
478 | sendId;
479 | if (typeof dom === 'string') {
480 | dom = document.getElementById(dom);
481 | }
482 | if (!dom) {
483 | that.emit("send_file_error", new Error("Can not find dom while sending file"), socketId);
484 | return;
485 | }
486 | if (!dom.files || !dom.files[0]) {
487 | that.emit("send_file_error", new Error("No file need to be sended"), socketId);
488 | return;
489 | }
490 | file = dom.files[0];
491 | that.fileChannels[socketId] = that.fileChannels[socketId] || {};
492 | sendId = that.getRandomString();
493 | fileToSend = {
494 | file: file,
495 | state: "ask"
496 | };
497 | that.fileChannels[socketId][sendId] = fileToSend;
498 | that.sendAsk(socketId, sendId, fileToSend);
499 | that.emit("send_file", sendId, socketId, file);
500 | };
501 |
502 | //发送多个文件的碎片
503 | skyrtc.prototype.sendFileChunks = function() {
504 | var socketId,
505 | sendId,
506 | that = this,
507 | nextTick = false;
508 | for (socketId in that.fileChannels) {
509 | for (sendId in that.fileChannels[socketId]) {
510 | if (that.fileChannels[socketId][sendId].state === "send") {
511 | nextTick = true;
512 | that.sendFileChunk(socketId, sendId);
513 | }
514 | }
515 | }
516 | if (nextTick) {
517 | setTimeout(function() {
518 | that.sendFileChunks();
519 | }, 10);
520 | }
521 | };
522 |
523 | //发送某个文件的碎片
524 | skyrtc.prototype.sendFileChunk = function(socketId, sendId) {
525 | var that = this,
526 | fileToSend = that.fileChannels[socketId][sendId],
527 | packet = {
528 | type: "__file",
529 | signal: "chunk",
530 | sendId: sendId
531 | },
532 | channel;
533 |
534 | fileToSend.sendedPackets++;
535 | fileToSend.packetsToSend--;
536 |
537 |
538 | if (fileToSend.fileData.length > packetSize) {
539 | packet.last = false;
540 | packet.data = fileToSend.fileData.slice(0, packetSize);
541 | packet.percent = fileToSend.sendedPackets / fileToSend.allPackets * 100;
542 | that.emit("send_file_chunk", sendId, socketId, fileToSend.sendedPackets / fileToSend.allPackets * 100, fileToSend.file);
543 | } else {
544 | packet.data = fileToSend.fileData;
545 | packet.last = true;
546 | fileToSend.state = "end";
547 | that.emit("sended_file", sendId, socketId, fileToSend.file);
548 | that.cleanSendFile(sendId, socketId);
549 | }
550 |
551 | channel = that.dataChannels[socketId];
552 |
553 | if (!channel) {
554 | that.emit("send_file_error", new Error("Channel has been destoried"), socketId, sendId, fileToSend.file);
555 | return;
556 | }
557 | channel.send(JSON.stringify(packet));
558 | fileToSend.fileData = fileToSend.fileData.slice(packet.data.length);
559 | };
560 |
561 | //发送文件请求后若对方同意接受,开始传输
562 | skyrtc.prototype.receiveFileAccept = function(sendId, socketId) {
563 | var that = this,
564 | fileToSend,
565 | reader,
566 | initSending = function(event, text) {
567 | fileToSend.state = "send";
568 | fileToSend.fileData = event.target.result;
569 | fileToSend.sendedPackets = 0;
570 | fileToSend.packetsToSend = fileToSend.allPackets = parseInt(fileToSend.fileData.length / packetSize, 10);
571 | that.sendFileChunks();
572 | };
573 | fileToSend = that.fileChannels[socketId][sendId];
574 | reader = new window.FileReader(fileToSend.file);
575 | reader.readAsDataURL(fileToSend.file);
576 | reader.onload = initSending;
577 | that.emit("send_file_accepted", sendId, socketId, that.fileChannels[socketId][sendId].file);
578 | };
579 |
580 | //发送文件请求后若对方拒绝接受,清除掉本地的文件信息
581 | skyrtc.prototype.receiveFileRefuse = function(sendId, socketId) {
582 | var that = this;
583 | that.fileChannels[socketId][sendId].state = "refused";
584 | that.emit("send_file_refused", sendId, socketId, that.fileChannels[socketId][sendId].file);
585 | that.cleanSendFile(sendId, socketId);
586 | };
587 |
588 | //清除发送文件缓存
589 | skyrtc.prototype.cleanSendFile = function(sendId, socketId) {
590 | var that = this;
591 | delete that.fileChannels[socketId][sendId];
592 | };
593 |
594 | //发送文件请求
595 | skyrtc.prototype.sendAsk = function(socketId, sendId, fileToSend) {
596 | var that = this,
597 | channel = that.dataChannels[socketId],
598 | packet;
599 | if (!channel) {
600 | that.emit("send_file_error", new Error("Channel has been closed"), socketId, sendId, fileToSend.file);
601 | }
602 | packet = {
603 | name: fileToSend.file.name,
604 | size: fileToSend.file.size,
605 | sendId: sendId,
606 | type: "__file",
607 | signal: "ask"
608 | };
609 | channel.send(JSON.stringify(packet));
610 | };
611 |
612 | //获得随机字符串来生成文件发送ID
613 | skyrtc.prototype.getRandomString = function() {
614 | return (Math.random() * new Date().getTime()).toString(36).toUpperCase().replace(/\./g, '-');
615 | };
616 |
617 | /***********************接收者部分***********************/
618 |
619 |
620 | //接收到文件碎片
621 | skyrtc.prototype.receiveFileChunk = function(data, sendId, socketId, last, percent) {
622 | var that = this,
623 | fileInfo = that.receiveFiles[sendId];
624 | if (!fileInfo.data) {
625 | fileInfo.state = "receive";
626 | fileInfo.data = "";
627 | }
628 | fileInfo.data = fileInfo.data || "";
629 | fileInfo.data += data;
630 | if (last) {
631 | fileInfo.state = "end";
632 | that.getTransferedFile(sendId);
633 | } else {
634 | that.emit("receive_file_chunk", sendId, socketId, fileInfo.name, percent);
635 | }
636 | };
637 |
638 | //接收到所有文件碎片后将其组合成一个完整的文件并自动下载
639 | skyrtc.prototype.getTransferedFile = function(sendId) {
640 | var that = this,
641 | fileInfo = that.receiveFiles[sendId],
642 | hyperlink = document.createElement("a"),
643 | mouseEvent = new MouseEvent('click', {
644 | view: window,
645 | bubbles: true,
646 | cancelable: true
647 | });
648 | hyperlink.href = fileInfo.data;
649 | hyperlink.target = '_blank';
650 | hyperlink.download = fileInfo.name || dataURL;
651 |
652 | hyperlink.dispatchEvent(mouseEvent);
653 | (window.URL || window.webkitURL).revokeObjectURL(hyperlink.href);
654 | that.emit("receive_file", sendId, fileInfo.socketId, fileInfo.name);
655 | that.cleanReceiveFile(sendId);
656 | };
657 |
658 | //接收到发送文件请求后记录文件信息
659 | skyrtc.prototype.receiveFileAsk = function(sendId, fileName, fileSize, socketId) {
660 | var that = this;
661 | that.receiveFiles[sendId] = {
662 | socketId: socketId,
663 | state: "ask",
664 | name: fileName,
665 | size: fileSize
666 | };
667 | that.emit("receive_file_ask", sendId, socketId, fileName, fileSize);
668 | };
669 |
670 | //发送同意接收文件信令
671 | skyrtc.prototype.sendFileAccept = function(sendId) {
672 | var that = this,
673 | fileInfo = that.receiveFiles[sendId],
674 | channel = that.dataChannels[fileInfo.socketId],
675 | packet;
676 | if (!channel) {
677 | that.emit("receive_file_error", new Error("Channel has been destoried"), sendId, socketId);
678 | }
679 | packet = {
680 | type: "__file",
681 | signal: "accept",
682 | sendId: sendId
683 | };
684 | channel.send(JSON.stringify(packet));
685 | };
686 |
687 | //发送拒绝接受文件信令
688 | skyrtc.prototype.sendFileRefuse = function(sendId) {
689 | var that = this,
690 | fileInfo = that.receiveFiles[sendId],
691 | channel = that.dataChannels[fileInfo.socketId],
692 | packet;
693 | if (!channel) {
694 | that.emit("receive_file_error", new Error("Channel has been destoried"), sendId, socketId);
695 | }
696 | packet = {
697 | type: "__file",
698 | signal: "refuse",
699 | sendId: sendId
700 | };
701 | channel.send(JSON.stringify(packet));
702 | that.cleanReceiveFile(sendId);
703 | };
704 |
705 | //清除接受文件缓存
706 | skyrtc.prototype.cleanReceiveFile = function(sendId) {
707 | var that = this;
708 | delete that.receiveFiles[sendId];
709 | };
710 |
711 | return new skyrtc();
712 | };
--------------------------------------------------------------------------------