Sweshi's Tutorials

Beginner Friendly WEB RTC Tutorial

Creating a Data Transfer Application Part 2 - Completing the Script (Advanced)

To actually send the data after the user chooses a file, the “sendFile” function is called when the user clicks the "Send File" button. It retrieves the selected file from the file input, reads its content as an ArrayBuffer, and converts it to Base64 using the arrayBufferToBase64 function. The file data, user's name, and file type are sent to the server as a JSON message via WebSocket. The file input is cleared after sending the file.

WebRTC sendFile() function

  • Line 82 – From the file input, we select the first file and store in our variable. Our file selection only supports one file so that will be the file selected.
  • Line 83 – This makes sure that some file has been selected and stored in the variable before we can go on to do anything with it.
  • Line 84 – We create an instance of the “FileReader” which is used to read file contents.
  • Line 86 – We use the file reader and setup an event handler that runs when it completes reading the file we are trying to send.
  • Line 87 – We collect the file type or resort to using a binary format that triggers a download.
  • Line 88 – The file content that has been read is stored as an “arrayBuffer”. This is then converted to Base64 encoding. This function will be also created later but we call it here.
  • Line 89 – We then use the Web Socket to send the data in Base64 encoding, the user name that sent it and the file type to the peer.
  • Line 92 – The input box on the webpage is cleared after sending the file.
  • Line 95 – Reads the file content as an array buffer. When this is completed then Line 86 executes.

arrayBufferToBase64 Conversion

The arrayBufferToBase64 function converts an ArrayBuffer to a Base64-encoded string. It processes the buffer in chunks to avoid stack size issues.

WebRTC arrayBufferToBase64 conversion

Remember that when the previous function in the previous screenshot line 88 calls the “arrayBufferToBase64.” This is the function. Its main purpose is to convert the arrayBuffer to Base64. This is how it works.

  • Line 99 – The function receives the array buffer from the “sendFile()” function.
  • Line 100 – We set the size for processing the buffer in 32KB chunks. We can use larger chunks but they are more error prone. So if we send a file of a specific size, it works through it chunk by chunk until the whole file is sent.
  • Line 101 – We then convert the array to an 8-bit unsigned integer.
  • Line 102 – We create an array that will store all the byte arrays. These are obtained after processing the buffer.
  • Line 104 – We then loop through the bytes array in chunks. Through this process, we are able to go through all the chunks making up the file we are transmitting.
  • Line 105 – We create a subarray or chunk of bytes with a specific starting(offset) and chunk size. We specify for example that the first chunk is starts at 0 bytes and is 32kb, the next chunk starts at 32kb and is 32kb, the next chunk starts at 64kb and is 32 kb and so on.
  • Line 106 – Each of the 32 byte chunks are then converted to a String using UTF-16 encoding and then added to the bytesArrays we created in line 102. The array will eventually store all the chunks for the whole file in string format.
  • Line 109 – we then combine the array content into a single string and that is converted to base64 using the btoa function (Binary to ASCII).

At this point, the file would be sent to the peer but to display it so that it can be downloaded, we need the displayFile() function

WebRTC displayFile() function

  • Line 72 – The function receives the data or content of the file, the name of the sender and the file type.
  • Line 73 – the Base64 data that was sent is converted to a blob format by supplying it the Base64 data and the file type. The function for converting is also a custom function that will be explain after this.
  • Line 74 – We then create an anchor tag that will be used as a download link.
  • Line 75 – We create a URL to the blob file and add the URL to the anchor tag filling up its “href” property.
  • Line 76 – We then set the download attribute of the anchor tag to specify the name of the user, the file itself and the file type.
  • Line 77 – The download link can then show who sent the file.
  • Line 78 – We add the anchor tag to the div “chatbox” which displays the download link to the user.

The b64toBlob function converts a Base64-encoded string to a Blob object. It processes Base64 data in slices to create a Uint8Array and then constructs a Blob from the byte arrays.

WebRTC b64toBlob

  • Line 113 – The function receives the Base64 data and content type from the displayFile() function. The slice size is set automatically.
  • Line 114 – using the “atob” function, the base64 data is decoded to into a string of byte characters.
  • Line 115 – We then create an empty array of bytes.
  • Line 117 – The byte characters are then looped through based on the slice size.
  • Line 118 – We then store each slice.
  • Line 120 – We create an array (byteNumbers) based on the slice size for storing the slice characters.
  • Line 121 – We loop through the characters in a slice.
  • Line 122 – For each character in the slice, the character code is obtained and stored in the byteNumbers array.
  • Line 125 – The byteNumbers array is used to create a Uint8Array (byteArray), representing a chunk of binary data.
  • Line 126 – The byteArray is then pushed into the byteArrays array.
  • Line 129 – The function uses the Blob constructor to create a Blob object (blob) from the array of Uint8Arrays (byteArrays). The type parameter specifies the content type of the Blob (e.g., MIME type)
  • Line 130 – We then return the Blob to the displayFile() function.

Lastly, we will finish off by making sure that the onmessage function which is very much the same as the texting application with only a few changes.

WebRTC onmessage and onclose functions

From the texting application, I have used the same code essentially but this time, I have created an alert so that when a user opens the link and connects, the peer can be alerted that the user is connected. This is seen in line 53.

  • Line 56 has a different if statement as well, it checks if the file content, file name and file type are all sent and then calls the displayFile() function that generates the link and adds it to the page.

The whole data.php document should look as shown below.

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WebRTC File Transfer Example</title> <style> #chatBox { width: 300px; margin: 20px; } #fileInput { margin-top: 10px; } </style> </head> <body> <h1>WebRTC File Transfer Example</h1> <div id="chatBox"></div> <input type="file" id="fileInput"> <button onclick="sendFile()">Send File</button> <script> const chatBox = document.getElementById('chatBox'); const fileInput = document.getElementById('fileInput'); let ws; let peer; // Prompt user for name const userName = prompt('Enter your name:'); if (!userName) { alert('Please enter a name.'); window.location.reload(); } function initWebSocket() { ws = new WebSocket('ws://192.168.43.141:3000'); ws.onopen = () => { console.log('WebSocket connection opened'); // Send the user's name to the server upon connection ws.send(JSON.stringify({ "name": userName })); }; ws.onmessage = async (event) => { try { const data = JSON.parse(await event.data.text()); if (data.name) { peer = data.name; alert(peer+" has connected"); } if (data.file && data.name && data.fileType) { // Handle file-related messages displayFile(data.file, data.name, data.fileType); } else { console.warn('Invalid message format:', data); } } catch (error) { console.error('Error parsing JSON:', error); } }; ws.onclose = () => { console.log('WebSocket connection closed'); }; } function displayFile(fileData, sender, fileType) { const blob = b64toBlob(fileData, fileType); const downloadLink = document.createElement('a'); downloadLink.href = URL.createObjectURL(blob); downloadLink.download = `${sender}_file.${fileType.split('/')[1] || 'bin'}`; downloadLink.textContent = `${sender} sent a file - Click to download`; chatBox.appendChild(downloadLink); } function sendFile() { const file = fileInput.files[0]; if (file) { const reader = new FileReader(); reader.onload = () => { const fileType = file.type || 'application/octet-stream'; const base64Data = arrayBufferToBase64(reader.result); ws.send(JSON.stringify({ "file": base64Data, "name": userName, "fileType": fileType })); // Clear the file input after sending the file fileInput.value = null; }; reader.readAsArrayBuffer(file); } } function arrayBufferToBase64(buffer) { const CHUNK_SIZE = 0x8000; // 32 KB chunks const bytes = new Uint8Array(buffer); const byteArrays = []; for (let offset = 0; offset < bytes.length; offset += CHUNK_SIZE) { const chunk = bytes.subarray(offset, offset + CHUNK_SIZE); byteArrays.push(String.fromCharCode.apply(null, chunk)); } return btoa(byteArrays.join('')); } function b64toBlob(b64Data, contentType = '', sliceSize = 512) { const byteCharacters = atob(b64Data); const byteArrays = []; for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) { const slice = byteCharacters.slice(offset, offset + sliceSize); const byteNumbers = new Array(slice.length); for (let i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } const byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } const blob = new Blob(byteArrays, { type: contentType }); return blob; } initWebSocket(); </script> </body> </html>

You can test it on your local area network or open two browsers and see if you are able to transmit. Remember to run the page from the webserver. If you test on 2 different computers and used a local IP address for the WebSocket connection, then make sure both computers are on the same network.

WebRTC final data transfer testing

The file should be able to send after both clients connect and insert their names.