Documentation Index
Fetch the complete documentation index at: https://docsdrop.erikraft.com/llms.txt
Use this file to discover all available pages before exploring further.
File Transfer Flow
ErikrafT Drop implements a sophisticated file transfer system that handles everything from small text messages to large multi-gigabyte files. The system uses WebRTC DataChannels for direct peer-to-peer transfers with WebSocket fallback when necessary.
Transfer Lifecycle Overview
File Processing
// From network.js lines 675-702
async requestFileTransfer(files) {
let header = [];
let totalSize = 0;
let imagesOnly = true;
for (let i = 0; i < files.length; i++) {
const file = files[i];
const fileHeader = {
name: file.name,
size: file.size,
mime: file.type || mime.defaultMime
};
header.push(fileHeader);
totalSize += file.size;
if (!mime.isImage(file)) {
imagesOnly = false;
}
}
this._filesRequested = files;
this.sendJSON({
type: 'request',
header: header,
totalSize: totalSize,
imagesOnly: imagesOnly
});
}
MIME Type Detection
The system automatically detects and adds missing MIME types:
// From network.js line 1464
let files = mime.addMissingMimeTypesToFiles([...message.files]);
Transfer Request Protocol
Request Message Structure
{
type: 'request',
header: [
{
name: 'document.pdf',
size: 1048576,
mime: 'application/pdf'
}
],
totalSize: 1048576,
imagesOnly: false
}
Request Handling
// From network.js lines 813-839
_onFilesTransferRequest(request) {
if (this._requestPending) {
this.sendJSON({type: 'files-transfer-response', accepted: false});
return;
}
if (window.iOS && request.totalSize >= 200*1024*1024) {
this.sendJSON({
type: 'files-transfer-response',
accepted: false,
reason: 'ios-memory-limit'
});
return;
}
this._requestPending = request;
Events.fire('files-transfer-request', {
request: request,
peerId: this._peerId
});
}
File Chunking System
FileChunker Class
The FileChunker class breaks large files into manageable chunks:
// From network.js lines 1668-1716
class FileChunker {
constructor(file, onChunk, onPartitionEnd) {
this._chunkSize = 64000; // 64 KB
this._maxPartitionSize = 1e6; // 1 MB
this._offset = 0;
this._partitionSize = 0;
this._file = file;
this._onChunk = onChunk;
this._onPartitionEnd = onPartitionEnd;
this._reader = new FileReader();
this._reader.addEventListener('load', e => this._onChunkRead(e.target.result));
}
}
Chunk Reading Process
// From network.js lines 1687-1702
_readChunk() {
const chunk = this._file.slice(this._offset, this._offset + this._chunkSize);
this._reader.readAsArrayBuffer(chunk);
}
_onChunkRead(chunk) {
this._offset += chunk.byteLength;
this._partitionSize += chunk.byteLength;
this._onChunk(chunk);
if (this.isFileEnd()) return;
if (this._isPartitionEnd()) {
this._onPartitionEnd(this._offset);
return;
}
this._readChunk();
}
Transfer Execution
// From network.js lines 729-748
async _sendFile(file) {
this.sendJSON({
type: 'header',
size: file.size,
name: file.name,
mime: file.type || mime.defaultMime
});
this._chunker = new FileChunker(
file,
chunk => this._send(chunk),
offset => this._onPartitionEnd(offset)
);
this._chunker.nextPartition();
}
Progress Tracking
// From network.js lines 763-765
_sendProgress(progress) {
this.sendJSON({ type: 'progress', progress: progress });
}
Partition Management
// From network.js lines 750-756
_onPartitionEnd(offset) {
this.sendJSON({ type: 'partition', offset: offset });
}
_onReceivedPartitionEnd(offset) {
this.sendJSON({ type: 'partition-received', offset: offset });
}
File Reception and Assembly
FileDigester Class
The FileDigester class reassembles received chunks:
// From network.js lines 1718-1746
class FileDigester {
constructor(meta, totalSize, totalBytesReceived, callback) {
this._buffer = [];
this._bytesReceived = 0;
this._size = meta.size;
this._name = meta.name;
this._mime = meta.mime;
this._totalSize = totalSize;
this._totalBytesReceived = totalBytesReceived;
this._callback = callback;
}
unchunk(chunk) {
this._buffer.push(chunk);
this._bytesReceived += chunk.byteLength || chunk.size;
this.progress = (this._totalBytesReceived + this._bytesReceived) / this._totalSize;
if (isNaN(this.progress)) this.progress = 1;
if (this._bytesReceived < this._size) return;
const blob = new Blob(this._buffer);
this._buffer = null;
this._callback(new File([blob], this._name, {
type: this._mime || "application/octet-stream",
lastModified: new Date().getTime()
}));
}
}
Message Types and Protocols
Transfer Control Messages
Request Message
{
type: 'request',
header: [...],
totalSize: number,
imagesOnly: boolean
}
Response Message
{
type: 'files-transfer-response',
accepted: boolean,
reason?: string
}
{
type: 'header',
name: string,
size: number,
mime: string
}
Progress Message
{
type: 'progress',
progress: number // 0.0 to 1.0
}
Partition Messages
{
type: 'partition',
offset: number
}
{
type: 'partition-received',
offset: number
}
Completion Messages
{
type: 'file-transfer-complete'
}
{
type: 'message-transfer-complete'
}
Error Handling and Recovery
Transfer Cancellation
// From network.js lines 841-847
_respondToFileTransferRequest(accepted) {
this.sendJSON({type: 'files-transfer-response', accepted: accepted});
if (accepted) {
this._requestAccepted = this._requestPending;
this._totalBytesReceived = 0;
this._filesReceived = [];
}
this._requestPending = null;
}
Connection Error Handling
// From network.js lines 869-873
_onChunkReceived(chunk) {
if (!this._digester) {
console.error("Received chunk but no digester active");
return;
}
this._digester.unchunk(chunk);
}
Transfer Completion
// From network.js lines 902-913
_onFileTransferCompleted() {
const fileBlob = this._filesReceived[0];
this._totalBytesReceived += fileBlob.size;
this._completeTransfer('receive', true);
this.sendJSON({type: 'file-transfer-complete'});
const sameSize = fileBlob.size === acceptedHeader.size;
const sameName = fileBlob.name === acceptedHeader.name;
Events.fire('file-received', {
file: fileBlob,
peerId: this._peerId,
imagesOnly: request.imagesOnly,
sameSize,
sameName
});
}
Memory Management
- Streaming: Files are processed in chunks to prevent memory overload
- Buffer Cleanup: Automatic cleanup of completed transfers
- iOS Limits: Special handling for iOS memory constraints
Network Efficiency
- Chunk Size: 64KB chunks balance memory usage and network efficiency
- Partition Tracking: 1MB partitions for progress reporting
- Binary Transfer: Direct binary data transfer via DataChannels
Progress Feedback
- Real-time Updates: Continuous progress reporting during transfer
- Partition Tracking: Granular progress for large files
- UI Integration: Seamless integration with user interface
WebSocket Fallback
When WebRTC is unavailable, transfers fall back to WebSocket:
// From network.js lines 1322-1327
_send(chunk) {
this.sendJSON({
type: 'ws-chunk',
chunk: arrayBufferToBase64(chunk)
});
}
Base64 Encoding
// From network.js lines 1454-1456
const messageJSON = JSON.parse(message);
if (messageJSON.type === 'ws-chunk') message = base64ToArrayBuffer(messageJSON.chunk);
This file transfer system provides robust, efficient file sharing with comprehensive error handling and fallback mechanisms for maximum compatibility.