Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
569 lines (482 sloc) 18.2 KB
<!-- Demo version: 2018.12.20 -->
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>FileSharing without using FileBufferReader</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link rel="shortcut icon" href="/demos/logo.png">
<link rel="stylesheet" href="/demos/stylesheet.css">
<script src="/demos/menu.js"></script>
<style type="text/css">
#files-container div {
border-bottom: 1px dotted black;
padding: 5px 10px;
text-align: center;
}
.checkmark {
display:none;
width: 15px;
vertical-align: middle;
}
</style>
</head>
<body>
<header>
<a class="logo" href="/demos/"><img src="/demos/logo.png" alt="RTCMultiConnection"></a>
<a href="/demos/" class="menu-explorer">Menu<img src="/demos/menu-icon.png" alt="Menu"></a>
<nav>
<li>
<a href="/demos/">Home</a>
</li>
<li>
<a href="https://www.rtcmulticonnection.org/docs/getting-started/">Getting Started</a>
</li>
<li>
<a href="https://www.rtcmulticonnection.org/FAQ/">FAQ</a>
</li>
<li>
<a href="https://www.youtube.com/playlist?list=PLPRQUXAnRydKdyun-vjKPMrySoow2N4tl">YouTube</a>
</li>
<li>
<a href="https://rtcmulticonnection.herokuapp.com/demos/">Demos</a>
</li>
<li>
<a href="https://github.com/muaz-khan/RTCMultiConnection/wiki">Wiki</a>
</li>
<li>
<a href="https://github.com/muaz-khan/RTCMultiConnection">Github</a>
</li>
</nav>
</header>
<h1>
FileSharing without using FileBufferReader
<p class="no-mobile">
This RTCMultiConnection demo tries to share files using <a target="_blank" href="https://github.com/muaz-khan/WebRTC-Experiment/tree/master/WebRTC-File-Sharing#filejs">File.js</a>
</p>
</h1>
<section class="make-center">
<div>
<input type="text" id="room-id" value="abcdef" autocorrect=off autocapitalize=off size=20>
<button id="open-room">Open Room</button>
<button id="join-room">Join Room</button>
<button id="open-or-join-room">Auto Open Or Join Room</button>
</div>
<div style="margin: 20px 0;">
<input type="file" id="btn-share-file" disabled>
</div>
<div id="files-container" style="margin: 20px 0;"></div>
<div id="room-urls" style="text-align: center;display: none;background: #F1EDED;margin: 15px -10px;border: 1px solid rgb(189, 189, 189);border-left: 0;border-right: 0; margin-top: 50px;"></div>
</section>
<script src="/dist/RTCMultiConnection.min.js"></script>
<script src="/node_modules/webrtc-adapter/out/adapter.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
// ......................................................
// .......................UI Code........................
// ......................................................
document.getElementById('open-room').onclick = function() {
disableInputButtons();
connection.open(document.getElementById('room-id').value, function(isRoomOpened, roomid, error) {
if(isRoomOpened === true) {
showRoomURL(connection.sessionid);
}
else {
disableInputButtons(true);
if(error === 'Room not available') {
alert('Someone already created this room. Please either join or create a separate room.');
return;
}
alert(error);
}
});
};
document.getElementById('join-room').onclick = function() {
disableInputButtons();
connection.join(document.getElementById('room-id').value, function(isJoinedRoom, roomid, error) {
if (error) {
disableInputButtons(true);
if(error === 'Room not available') {
alert('This room does not exist. Please either create it or wait for moderator to enter in the room.');
return;
}
alert(error);
}
});
};
document.getElementById('open-or-join-room').onclick = function() {
disableInputButtons();
connection.openOrJoin(document.getElementById('room-id').value, function(isRoomExist, roomid, error) {
if(error) {
disableInputButtons(true);
alert(error);
}
else if (connection.isInitiator === true) {
// if room doesn't exist, it means that current user will create the room
showRoomURL(roomid);
}
});
};
// ......................................................
// ..................RTCMultiConnection Code.............
// ......................................................
var connection = new RTCMultiConnection();
// by default, socket.io server is assumed to be deployed on your own URL
connection.socketURL = '/';
// comment-out below line if you do not have your own socket.io server
// connection.socketURL = 'https://rtcmulticonnection.herokuapp.com:443/';
connection.socketMessageEvent = 'filesharing-without-filebufferreader-demo';
connection.session = {
data: true
};
connection.sdpConstraints.mandatory = {
OfferToReceiveAudio: false,
OfferToReceiveVideo: false
};
// ..................................
// ALL below scripts are redundant!!!
// ..................................
function disableInputButtons(enable) {
document.getElementById('room-id').onkeyup();
document.getElementById('open-or-join-room').disabled = !enable;
document.getElementById('open-room').disabled = !enable;
document.getElementById('join-room').disabled = !enable;
document.getElementById('room-id').disabled = !enable;
}
// ......................................................
// ......................Handling Room-ID................
// ......................................................
function showRoomURL(roomid) {
var roomHashURL = '#' + roomid;
var roomQueryStringURL = '?roomid=' + roomid;
var html = '<h2>Unique URL for your room:</h2><br>';
html += 'Hash URL: <a href="' + roomHashURL + '" target="_blank">' + roomHashURL + '</a>';
html += '<br>';
html += 'QueryString URL: <a href="' + roomQueryStringURL + '" target="_blank">' + roomQueryStringURL + '</a>';
var roomURLsDiv = document.getElementById('room-urls');
roomURLsDiv.innerHTML = html;
roomURLsDiv.style.display = 'block';
}
(function() {
var params = {},
r = /([^&=]+)=?([^&]*)/g;
function d(s) {
return decodeURIComponent(s.replace(/\+/g, ' '));
}
var match, search = window.location.search;
while (match = r.exec(search.substring(1)))
params[d(match[1])] = d(match[2]);
window.params = params;
})();
var roomid = '';
if (localStorage.getItem(connection.socketMessageEvent)) {
roomid = localStorage.getItem(connection.socketMessageEvent);
} else {
roomid = connection.token();
}
var txtRoomId = document.getElementById('room-id');
txtRoomId.value = roomid;
txtRoomId.onkeyup = txtRoomId.oninput = txtRoomId.onpaste = function() {
localStorage.setItem(connection.socketMessageEvent, document.getElementById('room-id').value);
};
var hashString = location.hash.replace('#', '');
if (hashString.length && hashString.indexOf('comment-') == 0) {
hashString = '';
}
var roomid = params.roomid;
if (!roomid && hashString.length) {
roomid = hashString;
}
if (roomid && roomid.length) {
document.getElementById('room-id').value = roomid;
localStorage.setItem(connection.socketMessageEvent, roomid);
// auto-join-room
(function reCheckRoomPresence() {
connection.checkPresence(roomid, function(isRoomExist) {
if (isRoomExist) {
connection.join(roomid);
return;
}
setTimeout(reCheckRoomPresence, 5000);
});
})();
disableInputButtons();
}
// detect 2G
if(navigator.connection &&
navigator.connection.type === 'cellular' &&
navigator.connection.downlinkMax <= 0.115) {
alert('2G is not supported. Please use a better internet service.');
}
</script>
<script type="text/javascript">
document.getElementById('btn-share-file').onchange = function() {
if(!this.files.length) return;
var file = this.files[0];
// To Send a File
FileXYZ.Send({
file: file,
channel: connection,
interval: 100,
chunkSize: connection.chunkSize,
onBegin: fileHelper.onBegin,
onEnd: fileHelper.onEnd,
onProgress: fileHelper.onProgress
});
document.getElementById('btn-share-file').value = '';
};
connection.onopen = function() {
document.getElementById('btn-share-file').disabled = false;
};
// Muaz Khan - www.MuazKhan.com
// MIT License - www.WebRTC-Experiment.com/licence
// Documentation - github.com/muaz-khan/WebRTC-Experiment/tree/master/WebRTC-File-Sharing
// _______
// File.js
var FileXYZ = {
Send: function(config) {
var file = config.file;
var socket = config.channel;
var chunkSize = config.chunkSize || 40 * 1000; // 64k max sctp limit (AFAIK!)
var sliceId = 0;
var cacheSize = chunkSize;
var chunksPerSlice = Math.floor(Math.min(100000000, cacheSize) / chunkSize);
var sliceSize = chunksPerSlice * chunkSize;
var maxChunks = Math.ceil(file.size / chunkSize);
// uuid is used to uniquely identify sending instance
var uuid = file.uuid || (Math.random() * new Date().getTime()).toString(36).toUpperCase().replace(/\./g, '-');
socket.send({
uuid: uuid,
maxChunks: maxChunks,
size: file.size,
name: file.name,
lastModifiedDate: file.lastModifiedDate,
type: file.type,
start: true
});
file.maxChunks = maxChunks;
file.uuid = uuid;
if (config.onBegin) config.onBegin(file);
var blob, reader = new FileReader();
reader.onloadend = function(evt) {
if (evt.target.readyState == FileReader.DONE) {
addChunks(file.name, evt.target.result, function() {
sliceId++;
if ((sliceId + 1) * sliceSize < file.size) {
blob = file.slice(sliceId * sliceSize, (sliceId + 1) * sliceSize);
reader.readAsArrayBuffer(blob);
} else if (sliceId * sliceSize < file.size) {
blob = file.slice(sliceId * sliceSize, file.size);
reader.readAsArrayBuffer(blob);
} else {
socket.send({
uuid: uuid,
maxChunks: maxChunks,
size: file.size,
name: file.name,
lastModifiedDate: file.lastModifiedDate,
type: file.type,
end: true
});
file.url = URL.createObjectURL(file);
if (config.onEnd) config.onEnd(file);
}
});
}
};
blob = file.slice(sliceId * sliceSize, (sliceId + 1) * sliceSize);
reader.readAsArrayBuffer(blob);
var numOfChunksInSlice;
var currentPosition = 0;
var hasEntireFile;
var chunks = [];
function addChunks(fileName, binarySlice, callback) {
numOfChunksInSlice = Math.ceil(binarySlice.byteLength / chunkSize);
for (var i = 0; i < numOfChunksInSlice; i++) {
var start = i * chunkSize;
chunks[currentPosition] = binarySlice.slice(start, Math.min(start + chunkSize, binarySlice.byteLength));
FileConverterXYZ.ArrayBufferToDataURL(chunks[currentPosition], function(str) {
socket.send({
uuid: uuid,
value: str,
currentPosition: currentPosition,
maxChunks: maxChunks
});
});
currentPosition++;
}
if (config.onProgress) {
config.onProgress({
currentPosition: currentPosition,
maxChunks: maxChunks,
uuid: uuid,
size: chunkSize,
sending: true
});
}
if (currentPosition == maxChunks) {
hasEntireFile = true;
}
if (config.interval == 0 || typeof config.interval == 'undefined')
callback();
else
setTimeout(callback, config.interval);
}
},
Receiver: function(config) {
var packets = {};
function receive(chunk) {
if (chunk.start && !packets[chunk.uuid]) {
packets[chunk.uuid] = [];
if (config.onBegin) config.onBegin(chunk);
}
if (!chunk.end && chunk.value) {
packets[chunk.uuid].push(chunk.value);
}
if (chunk.end) {
var _packets = packets[chunk.uuid];
var finalArray = [],
length = _packets.length;
for (var i = 0; i < length; i++) {
if (!!_packets[i]) {
FileConverterXYZ.DataURLToBlob(_packets[i], function(buffer) {
finalArray.push(buffer);
});
}
}
var blob = new Blob(finalArray, {
type: chunk.type
});
blob = mergeXYZ(blob, chunk);
blob.url = URL.createObjectURL(blob);
blob.uuid = chunk.uuid;
if (!blob.size) console.error('Something went wrong. Blob Size is 0.');
if (config.onEnd) config.onEnd(blob);
}
if (chunk.value && config.onProgress) {
chunk.size = 40 * 1000;
chunk.receiving = true;
config.onProgress(chunk);
}
}
return {
receive: receive
};
},
SaveToDisk: function(fileUrl, fileName) {
var hyperlink = document.createElement('a');
hyperlink.href = fileUrl;
hyperlink.target = '_blank';
hyperlink.download = fileName || fileUrl;
var mouseEvent = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
});
hyperlink.dispatchEvent(mouseEvent);
(window.URL || window.webkitURL).revokeObjectURL(hyperlink.href);
}
};
// ________________
// FileConverterXYZ.js
var FileConverterXYZ = {
ArrayBufferToDataURL: function(buffer, callback) {
window.BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder;
// getting blob from array-buffer
var blob = new Blob([buffer]);
// reading file as binary-string
var fileReader = new FileReader();
fileReader.onload = function(e) {
callback(e.target.result);
};
fileReader.readAsDataURL(blob);
},
DataURLToBlob: function(dataURL, callback) {
var binary = atob(dataURL.substr(dataURL.indexOf(',') + 1)),
i = binary.length,
view = new Uint8Array(i);
while (i--) {
view[i] = binary.charCodeAt(i);
}
callback(new Blob([view]));
}
};
function mergeXYZ(mergein, mergeto) {
if (!mergein) mergein = {};
if (!mergeto) return mergein;
for (var item in mergeto) {
mergein[item] = mergeto[item];
}
return mergein;
}
// File.js codes usage
var progressHelperXYZ = {};
var fileHelper = {
onBegin: function(file) {
var div = document.createElement('div');
div.title = file.name;
div.innerHTML = '<label></label><progress min=0 max=100 value=0></progress>';
var outputPanel = document.getElementById('files-container');
outputPanel.insertBefore(div, outputPanel.firstChild);
progressHelperXYZ[file.uuid] = {
div: div,
progress: div.querySelector('progress'),
label: div.querySelector('label')
};
progressHelperXYZ[file.uuid].progress.max = file.maxChunks;
},
onEnd: function(file) {
progressHelperXYZ[file.uuid].div.innerHTML = getFileHTML(file);
},
onProgress: function(chunk) {
var helper = progressHelperXYZ[chunk.uuid];
helper.progress.value = chunk.currentPosition || chunk.maxChunks || helper.progress.max;
updateLabelXYZ(helper.progress, helper.label);
}
};
function updateLabelXYZ(progress, label) {
if (progress.position == -1) return;
var position = +progress.position.toFixed(2).split('.')[1] || 100;
label.innerHTML = position + '%';
}
// To Receive a File
var fleReceiver = new FileXYZ.Receiver(fileHelper);
connection.onmessage = function(event) {
event.data = JSON.parse(event.data);
fleReceiver.receive(event.data);
};
function getFileHTML(file) {
var url = file.url || URL.createObjectURL(file);
var attachment = '<a href="' + url + '" target="_blank" download="' + file.name + '">Download: <b>' + file.name + '</b></a><br>';
if (file.name.match(/\.jpg|\.png|\.jpeg|\.gif/gi)) {
attachment += '<br><img crossOrigin="anonymous" src="' + url + '">';
} else if (file.name.match(/\.wav|\.mp3/gi)) {
attachment += '<br><audio src="' + url + '" controls></audio>';
} else if (file.name.match(/\.pdf|\.js|\.txt|\.sh/gi)) {
attachment += '<iframe class="inline-iframe" src="' + url + '"></iframe></a>';
}
return attachment;
}
connection._send = connection.send;
connection.send = function(data, remoteUserId) {
data = JSON.stringify(data);
connection._send(data, remoteUserId);
};
</script>
<section>
<p>
You can write cordova/ionic/phonegap based <a href="https://www.rtcmulticonnection.org/docs/Write-iOS-Apps/" target="_blank">iOS</a> or <a href="https://www.rtcmulticonnection.org/docs/Write-Android-Apps/" target="_blank">Android</a> apps for this demo as well.
</p>
<p>
You can run same demo on Safari-11 as well. (both on MacOSX & iOS)
</p>
<p>
Microsoft Edge is also supported.
</p>
</section>
<footer>
<small id="send-message"></small>
</footer>
<script src="https://cdn.webrtc-experiment.com/common.js"></script>
</body>
</html>