使用Webrtc和React Js在网络上共享跨平台的点对点文件
我们希望实现一个零思想的文件传输机制,即在两个设备或个人之间共享文件,不需要考虑如何、在哪里、为什么和什么。
“WebRTC是一个免费的开放项目,通过简单的APIs为浏览器与移动应用程序提供实时通信(RTC)功能。WebRTC组件已经进行了优化,以更好地满足这一目的。” webrtc.org
const express = require("express");
const http = require("http");
const app = express();
const server = http.createServer(app);
const socket = require("socket.io");
const io = socket(server);
const users = {};
const socketToRoom = {};
io.on('connection', socket => {
socket.on("join room", roomID => {
if (users[roomID]) {
const length = users[roomID].length;
if (length === 2) {
socket.emit("room full");
return;
}
users[roomID].push(socket.id);
} else {
users[roomID] = [socket.id];
}
socketToRoom[socket.id] = roomID;
const usersInThisRoom = users[roomID].filter(id => id !== socket.id);
socket.emit("all users", usersInThisRoom);
});
socket.on("sending signal", payload => {
io.to(payload.userToSignal).emit('user joined', { signal: payload.signal, callerID: payload.callerID });
});
socket.on("returning signal", payload => {
io.to(payload.callerID).emit('receiving returned signal', { signal: payload.signal, id: socket.id });
});
socket.on('disconnect', () => {
const roomID = socketToRoom[socket.id];
let room = users[roomID];
if (room) {
room = room.filter(id => id !== socket.id);
users[roomID] = room;
socket.broadcast.emit('user left', socket.id);
}
});
});
server.listen(process.env.PORT || 8000, () => console.log('server is running on port 8000'));
import React, { useEffect, useRef, useState } from "react";
import io from "socket.io-client";
import Peer from "simple-peer";
import styled from "styled-components";
import streamSaver from "streamsaver";
const Container = styled.div`
padding: 20px;
display: flex;
height: 100vh;
width: 90%;
margin: auto;
flex-wrap: wrap;
`;
const worker = new Worker("../worker.js");
const Room = (props) => {
const [connectionEstablished, setConnection] = useState(false);
const [file, setFile] = useState();
const [gotFile, setGotFile] = useState(false);
const chunksRef = useRef([]);
const socketRef = useRef();
const peersRef = useRef([]);
const peerRef = useRef();
const fileNameRef = useRef("");
const roomID = props.match.params.roomID;
useEffect(() => {
socketRef.current = io.connect("/");
socketRef.current.emit("join room", roomID);
socketRef.current.on("all users", users => {
peerRef.current = createPeer(users[0], socketRef.current.id);
});
socketRef.current.on("user joined", payload => {
peerRef.current = addPeer(payload.signal, payload.callerID);
});
socketRef.current.on("receiving returned signal", payload => {
peerRef.current.signal(payload.signal);
setConnection(true);
});
socketRef.current.on("room full", () => {
alert("room is full");
})
}, []);
function createPeer(userToSignal, callerID) {
const peer = new Peer({
initiator: true,
trickle: false,
});
peer.on("signal", signal => {
socketRef.current.emit("sending signal", { userToSignal, callerID, signal });
});
peer.on("data", handleReceivingData);
return peer;
}
function addPeer(incomingSignal, callerID) {
const peer = new Peer({
initiator: false,
trickle: false,
});
peer.on("signal", signal => {
socketRef.current.emit("returning signal", { signal, callerID });
});
peer.on("data", handleReceivingData);
peer.signal(incomingSignal);
setConnection(true);
return peer;
}
function handleReceivingData(data) {
if (data.toString().includes("done")) {
setGotFile(true);
const parsed = JSON.parse(data);
fileNameRef.current = parsed.fileName;
} else {
worker.postMessage(data);
}
}
function download() {
setGotFile(false);
worker.postMessage("download");
worker.addEventListener("message", event => {
const stream = event.data.stream();
const fileStream = streamSaver.createWriteStream(fileNameRef.current);
stream.pipeTo(fileStream);
})
}
function selectFile(e) {
setFile(e.target.files[0]);
}
function sendFile() {
const peer = peerRef.current;
const stream = file.stream();
const reader = stream.getReader();
reader.read().then(obj => {
handlereading(obj.done, obj.value);
});
function handlereading(done, value) {
if (done) {
peer.write(JSON.stringify({ done: true, fileName: file.name }));
return;
}
peer.write(value);
reader.read().then(obj => {
handlereading(obj.done, obj.value);
})
}
}
let body;
if (connectionEstablished) {
body = (
<div>
<input onChange={selectFile} type="file" />
<button onClick={sendFile}>Send file</button>
</div>
);
} else {
body = (
<h1>Once you have a peer connection, you will be able to share files</h1>
);
}
let downloadPrompt;
if (gotFile) {
downloadPrompt = (
<div>
<span>You have received a file. Would you like to download the file?</span>
<button onClick={download}>Yes</button>
</div>
);
}
return (
<Container>
{body}
{downloadPrompt}
</Container>
);
};
export default Room;
let array = [];
self.addEventListener("message", event => {
if (event.data === "download") {
const blob = new Blob(array);
self.postMessage(blob);
array = [];
} else if (event.data === "abort") {
array = [];
} else {
array.push(event.data);
}
})
支持几乎所有的浏览器
支持庞大的文档大小——正如前面提到的,这是我们为什么要实现它的基本解释。
一个更好的方法来破译所发送信息的度量——通过在缓冲区中发送一个记录,我们现在可以显示信息,例如,发送的文档的级别,发送记录的速度等等。
识别未完成发送的文件——在无法完全发送文件的情况下,现在能够以不同的方式获取和处理文件。
信令服务器(STUN和TURN服务器)。
使多个对等连接可拓展。
当WebRTC不能工作时才用的一种混合共享方式。
提高传输效率和速度。
点击【阅读原文】了解更多详细信息