diff --git a/frontend/hooks/useFileTransferHandler.ts b/frontend/hooks/useFileTransferHandler.ts index 86b8764..7029003 100644 --- a/frontend/hooks/useFileTransferHandler.ts +++ b/frontend/hooks/useFileTransferHandler.ts @@ -114,14 +114,14 @@ export function useFileTransferHandler({ ); if (fileToDownload) { - // 检查文件是否为空 + // Check if file is empty if (fileToDownload.size === 0) { postLogToBackend( `[Firefox Debug] ERROR: File has 0 size! This explains the 0-byte download.` ); } - // 检查文件是否为有效的Blob + // Check if file is a valid Blob if (!(fileToDownload instanceof Blob)) { postLogToBackend( `[Firefox Debug] WARNING: File is not a Blob object, type: ${typeof fileToDownload}` @@ -131,7 +131,7 @@ export function useFileTransferHandler({ downloadAs(fileToDownload, fileToDownload.name); return true; } else { - // 调试日志:记录未找到文件的情况 + // Debug log: Record the case where file is not found const availableFileNames = latestRetrievedFiles.map((f) => f.name); postLogToBackend( `[Firefox Debug] File NOT found! Looking for: "${ diff --git a/frontend/lib/browserUtils.ts b/frontend/lib/browserUtils.ts index 6566f1e..3504d99 100644 --- a/frontend/lib/browserUtils.ts +++ b/frontend/lib/browserUtils.ts @@ -1,26 +1,27 @@ /** - * 浏览器检测工具函数 - * 扩展以支持Firefox WebRTC兼容性处理 + * Browser detection utility functions + * Extended to support Firefox WebRTC compatibility handling */ /** - * 检测是否为 Chrome 浏览器 - * @returns {boolean} 如果是 Chrome 返回 true,否则返回 false + * Detect if the browser is Chrome + * @returns {boolean} Returns true if it's Chrome, otherwise false */ export const isChrome = (): boolean => { - // 检测 Chrome 浏览器,排除基于 Chromium 的 Edge + // Detect Chrome browser, excluding Chromium-based Edge const userAgent = navigator.userAgent; return ( - userAgent.includes("Chrome") && !userAgent.includes("Edg") // 排除 Edge + userAgent.includes("Chrome") && !userAgent.includes("Edg") // Exclude Edge ); }; /** - * 检测是否支持程序化下载 - * Chrome 支持长时间传输后的自动下载,其他浏览器可能有限制 - * @returns {boolean} 如果支持自动下载返回 true + * Detect if programmatic download is supported + * Chrome supports automatic download after long transfers, other browsers may have limitations + * @returns {boolean} Returns true if automatic download is supported */ + export const supportsAutoDownload = (): boolean => { return isChrome(); }; diff --git a/frontend/lib/fileReceiver.ts b/frontend/lib/fileReceiver.ts index 0c2ecc3..991a57b 100644 --- a/frontend/lib/fileReceiver.ts +++ b/frontend/lib/fileReceiver.ts @@ -1,9 +1,9 @@ -// 🚀 新流程 - 接收端主导的文件传输: -// 1. 接收文件元数据 (fileMetadata) -// 2. 用户点击下载,发送文件请求 (fileRequest) -// 3. 接收所有数据块,自动检测完整性 -// 4. 完成Store同步后,主动发送完成确认 (fileReceiveComplete/folderReceiveComplete) -// 文件夹传输:重复单文件流程,最后发送文件夹完成确认 +// 🚀 New Process - Receiver-Dominated File Transfer: +// 1. Receive file metadata (fileMetadata) +// 2. User clicks download, send file request (fileRequest) +// 3. Receive all data chunks, automatically detect integrity +// 4. After completing Store synchronization, proactively send completion confirmation (fileReceiveComplete/folderReceiveComplete) +// Folder Transfer: Repeat single file process, finally send folder completion confirmation import { SpeedCalculator } from "@/lib/speedCalculator"; import WebRTC_Recipient from "./webrtc_Recipient"; import { @@ -22,14 +22,14 @@ import { EmbeddedChunkMeta, } from "@/types/webrtc"; import { postLogToBackend } from "@/app/config/api"; - +const developmentEnv = process.env.NEXT_PUBLIC_development!; /** - * 🚀 严格按序缓冲写入管理器 - 优化大文件磁盘I/O性能 + * 🚀 Strict Sequential Buffering Writer - Optimizes large file disk I/O performance */ class SequencedDiskWriter { private writeQueue = new Map(); private nextWriteIndex = 0; - private readonly maxBufferSize = 100; // 最多缓冲100个chunk(约6.4MB) + private readonly maxBufferSize = 100; // Buffer up to 100 chunks (approximately 6.4MB) private readonly stream: FileSystemWritableFileStream; private totalWritten = 0; @@ -38,55 +38,61 @@ class SequencedDiskWriter { this.nextWriteIndex = startIndex; } - /** - * 写入一个chunk,自动管理顺序和缓冲 - */ + /**\n * Write a chunk, automatically managing order and buffering\n */ async writeChunk(chunkIndex: number, chunk: ArrayBuffer): Promise { - // 1. 如果是期待的下一个chunk,立即写入 + // 1. If it is the expected next chunk, write immediately if (chunkIndex === this.nextWriteIndex) { await this.flushSequentialChunks(chunk); return; } - // 2. 如果是未来的chunk,缓冲起来 + // 2. If it's a future chunk, buffer it if (chunkIndex > this.nextWriteIndex) { if (this.writeQueue.size < this.maxBufferSize) { this.writeQueue.set(chunkIndex, chunk); - postLogToBackend( - `[DEBUG] 📦 BUFFERED chunk #${chunkIndex} (waiting for #${this.nextWriteIndex}), queue: ${this.writeQueue.size}/${this.maxBufferSize}` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] 📦 BUFFERED chunk #${chunkIndex} (waiting for #${this.nextWriteIndex}), queue: ${this.writeQueue.size}/${this.maxBufferSize}` + ); + } } else { - // 缓冲区满,强制处理最早的chunk以释放空间 + // Buffer full, forcing processing of the earliest chunk to free up space await this.forceFlushOldest(); this.writeQueue.set(chunkIndex, chunk); - postLogToBackend( - `[DEBUG] ⚠️ BUFFER_FULL, forced flush and buffered chunk #${chunkIndex}` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ⚠️ BUFFER_FULL, forced flush and buffered chunk #${chunkIndex}` + ); + } } return; } - // 3. 如果是过期的chunk,记录警告但忽略(已写入) - postLogToBackend( - `[DEBUG] ⚠️ DUPLICATE chunk #${chunkIndex} ignored (already written #${this.nextWriteIndex})` - ); + // 3. If the chunk is expired, log a warning but ignore (already written) + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ⚠️ DUPLICATE chunk #${chunkIndex} ignored (already written #${this.nextWriteIndex})` + ); + } } /** - * 写入当前chunk并尝试连续写入后续的chunk + * Write current chunk and attempt to sequentially write subsequent chunks */ private async flushSequentialChunks(firstChunk: ArrayBuffer): Promise { - // 写入当前chunk + // Write current chunk await this.stream.write(firstChunk); this.totalWritten += firstChunk.byteLength; - postLogToBackend( - `[DEBUG] ✓ DISK_WRITE chunk #${this.nextWriteIndex} - ${firstChunk.byteLength} bytes, total: ${this.totalWritten}` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ✓ DISK_WRITE chunk #${this.nextWriteIndex} - ${firstChunk.byteLength} bytes, total: ${this.totalWritten}` + ); + } this.nextWriteIndex++; - // 尝试连续写入缓冲中的chunk + // Try to sequentially write chunks from buffer let flushCount = 0; while (this.writeQueue.has(this.nextWriteIndex)) { const chunk = this.writeQueue.get(this.nextWriteIndex)!; @@ -99,14 +105,16 @@ class SequencedDiskWriter { } if (flushCount > 0) { - postLogToBackend( - `[DEBUG] 🔥 SEQUENTIAL_FLUSH ${flushCount} chunks, now at #${this.nextWriteIndex}, queue: ${this.writeQueue.size}` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] 🔥 SEQUENTIAL_FLUSH ${flushCount} chunks, now at #${this.nextWriteIndex}, queue: ${this.writeQueue.size}` + ); + } } } /** - * 强制刷新最早的chunk以释放缓冲区空间 + * Force refresh the earliest chunk to release buffer space */ private async forceFlushOldest(): Promise { if (this.writeQueue.size === 0) return; @@ -114,24 +122,26 @@ class SequencedDiskWriter { const oldestIndex = Math.min(...Array.from(this.writeQueue.keys())); const chunk = this.writeQueue.get(oldestIndex)!; - // 警告:非序写入 - postLogToBackend( - `[DEBUG] ⚠️ FORCE_FLUSH out-of-order chunk #${oldestIndex} (expected #${this.nextWriteIndex})` - ); + // Warning: Unordered write + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ⚠️ FORCE_FLUSH out-of-order chunk #${oldestIndex} (expected #${this.nextWriteIndex})` + ); + } - // 使用seek在正确位置写入(降级处理) - const fileOffset = oldestIndex * 65536; // 假设每个chunk 64KB + // Use seek to write at the correct position (fallback handling) + const fileOffset = oldestIndex * 65536; // Assume each chunk is 64KB await this.stream.seek(fileOffset); await this.stream.write(chunk); this.writeQueue.delete(oldestIndex); - // 恢复到当前位置 + // Return to current position const currentOffset = this.nextWriteIndex * 65536; await this.stream.seek(currentOffset); } /** - * 获取缓冲区状态 + * Get buffer status */ getBufferStatus(): { queueSize: number; @@ -146,10 +156,10 @@ class SequencedDiskWriter { } /** - * 关闭并清理资源 + * Close and clean up resources */ async close(): Promise { - // 尝试刷新所有剩余的chunk + // Try to flush all remaining chunks const remainingIndexes = Array.from(this.writeQueue.keys()).sort( (a, b) => a - b ); @@ -158,35 +168,35 @@ class SequencedDiskWriter { const fileOffset = chunkIndex * 65536; await this.stream.seek(fileOffset); await this.stream.write(chunk); - postLogToBackend( - `[DEBUG] 💾 FINAL_FLUSH chunk #${chunkIndex} at cleanup` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] 💾 FINAL_FLUSH chunk #${chunkIndex} at cleanup` + ); + } } this.writeQueue.clear(); } } -/** - * 🚀 新版本:管理按序列化融合数据包的文件接收状态 - */ +/**\n * 🚀 New Version: Manage file reception state for serialized embedded packets\n */ interface ActiveFileReception { meta: fileMetadata; // If meta is present, it means this file is currently being received; null means no file is being received. - chunks: (ArrayBuffer | null)[]; // 按序号排列的数据块数组 + chunks: (ArrayBuffer | null)[]; // Array of data chunks arranged by index receivedSize: number; initialOffset: number; // For resuming downloads fileHandle: FileSystemFileHandle | null; // Object related to writing to disk -- current file. writeStream: FileSystemWritableFileStream | null; // Object related to writing to disk. - sequencedWriter: SequencedDiskWriter | null; // 🚀 新增:严格按序写入管理器 + sequencedWriter: SequencedDiskWriter | null; // 🚀 Added: Strict sequential writing manager completionNotifier: { resolve: () => void; reject: (reason?: any) => void; }; - // 🚀 新版本:简化的按序接收管理 - receivedChunksCount: number; // 实际接收到的chunk数量 - expectedChunksCount: number; // 预期的chunk数量 - chunkSequenceMap: Map; // 跟踪哪些chunk已经接收(用于chunk序号) - isFinalized?: boolean; // 防止重复finalize的标记 + // 🚀 New Version: Simplified sequential reception management + receivedChunksCount: number; // Actual number of chunks received + expectedChunksCount: number; // Expected number of chunks + chunkSequenceMap: Map; // Track which chunks have been received (for chunk numbering) + isFinalized?: boolean; // Flag to prevent duplicate finalize operations } class FileReceiver { @@ -253,7 +263,7 @@ class FileReceiver { } if (this.activeFileReception) { - // 🚀 在错误时也要清理SequencedWriter + // 🚀 Also clean up SequencedWriter on error if (this.activeFileReception.sequencedWriter) { this.activeFileReception.sequencedWriter.close().catch((err) => { this.log( @@ -343,18 +353,18 @@ class FileReceiver { } const receptionPromise = new Promise((resolve, reject) => { - const expectedChunksCount = Math.ceil((fileInfo.size - offset) / 65536); // 计算预期chunk数量 + const expectedChunksCount = Math.ceil((fileInfo.size - offset) / 65536); // Calculate expected chunk count this.activeFileReception = { meta: fileInfo, - chunks: new Array(expectedChunksCount).fill(null), // 🚀 初始化为按索引排列的空数组 + chunks: new Array(expectedChunksCount).fill(null), // 🚀 Initialize as an empty array arranged by index receivedSize: 0, initialOffset: offset, fileHandle: null, writeStream: null, - sequencedWriter: null, // 🚀 新增:严格按序写入管理器 + sequencedWriter: null, // 🚀 Added: Strict sequential writing manager completionNotifier: { resolve, reject }, - // 🚀 新版本:简化的按序接收管理 + // 🚀 New Version: Simplified sequential reception management receivedChunksCount: 0, expectedChunksCount: expectedChunksCount, chunkSequenceMap: new Map(), @@ -370,9 +380,11 @@ class FileReceiver { this.webrtcConnection.sendData(JSON.stringify(request), this.peerId); this.log("log", "Sent fileRequest", { request }); } else { - postLogToBackend( - `[Firefox Debug] ERROR: Cannot send fileRequest - no peerId available!` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ERROR: Cannot send fileRequest - no peerId available!` + ); + } } return receptionPromise; @@ -433,18 +445,20 @@ class FileReceiver { } this.currentFolderName = null; - // 🚀 新流程:发送文件夹接收完成确认 - // 收集所有成功完成的文件ID + // 🚀 New Process: Send folder reception completion confirmation + // Collect all successfully completed file IDs const completedFileIds = folderProgress.fileIds.filter((fileId) => { - // 这里可以添加更复杂的验证逻辑,现在简单假设都成功了 + // More complex validation logic can be added here, now simply assume all succeeded return true; }); - postLogToBackend( - `[Firefox Debug] 📁 All files in folder completed - ${folderName}, files: ${completedFileIds.length}/${folderProgress.fileIds.length}` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] 📁 All files in folder completed - ${folderName}, files: ${completedFileIds.length}/${folderProgress.fileIds.length}` + ); + } - // 发送文件夹完成消息 + // Send folder completion message this.sendFolderReceiveComplete(folderName, completedFileIds, true); } // endregion @@ -452,8 +466,8 @@ class FileReceiver { // region WebRTC Data Handlers /** - * 将各种二进制数据格式转换为ArrayBuffer - * 支持Firefox的Blob、Uint8Array等格式 + * Convert various binary data formats to ArrayBuffer + * Supports Blob, Uint8Array, and other formats for Firefox */ private async convertToArrayBuffer(data: any): Promise { const originalType = Object.prototype.toString.call(data); @@ -464,13 +478,17 @@ class FileReceiver { try { const arrayBuffer = await data.arrayBuffer(); if (data.size !== arrayBuffer.byteLength) { - postLogToBackend( - `[DEBUG] ⚠️ Blob size mismatch: ${data.size}→${arrayBuffer.byteLength}` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ⚠️ Blob size mismatch: ${data.size}→${arrayBuffer.byteLength}` + ); + } } return arrayBuffer; } catch (error) { - postLogToBackend(`[DEBUG] ❌ Blob conversion failed: ${error}`); + if (developmentEnv === "true") { + postLogToBackend(`[DEBUG] ❌ Blob conversion failed: ${error}`); + } return null; } } else if (data instanceof Uint8Array || ArrayBuffer.isView(data)) { @@ -483,66 +501,82 @@ class FileReceiver { new Uint8Array(newArrayBuffer).set(uint8Array); return newArrayBuffer; } catch (error) { - postLogToBackend(`[DEBUG] ❌ TypedArray conversion failed: ${error}`); + if (developmentEnv === "true") { + postLogToBackend(`[DEBUG] ❌ TypedArray conversion failed: ${error}`); + } return null; } } else { - postLogToBackend( - `[DEBUG] ❌ Unknown data type: ${Object.prototype.toString.call(data)}` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ❌ Unknown data type: ${Object.prototype.toString.call( + data + )}` + ); + } return null; } } /** - * 🚀 新增:解析融合数据包 - * 格式: [4字节长度] + [JSON元数据] + [实际chunk数据] + * 🚀 Parsing fusion packets + * Format: [4 bytes length] + [JSON metadata] + [actual chunk data] */ private parseEmbeddedChunkPacket(arrayBuffer: ArrayBuffer): { chunkMeta: EmbeddedChunkMeta; chunkData: ArrayBuffer; } | null { try { - // 1. 检查数据包最小长度 + // 1. Check minimum packet length if (arrayBuffer.byteLength < 4) { - postLogToBackend( - `[DEBUG] ❌ Invalid embedded packet - too small: ${arrayBuffer.byteLength}` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ❌ Invalid embedded packet - too small: ${arrayBuffer.byteLength}` + ); + } return null; } - // 2. 读取元数据长度(4字节) + // 2. Read metadata length (4 bytes) const lengthView = new Uint32Array(arrayBuffer, 0, 1); const metaLength = lengthView[0]; - // 3. 验证数据包的完整性 + // 3. Verify packet integrity const expectedTotalLength = 4 + metaLength; if (arrayBuffer.byteLength < expectedTotalLength) { - postLogToBackend( - `[DEBUG] ❌ Incomplete embedded packet - expected: ${expectedTotalLength}, got: ${arrayBuffer.byteLength}` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ❌ Incomplete embedded packet - expected: ${expectedTotalLength}, got: ${arrayBuffer.byteLength}` + ); + } return null; } - // 4. 提取元数据部分 + // 4. Extract metadata section const metaBytes = new Uint8Array(arrayBuffer, 4, metaLength); const metaJson = new TextDecoder().decode(metaBytes); const chunkMeta: EmbeddedChunkMeta = JSON.parse(metaJson); - // 5. 提取实际chunk数据部分 + // 5. Extract actual chunk data section const chunkDataStart = 4 + metaLength; const chunkData = arrayBuffer.slice(chunkDataStart); - // 6. 验证chunk数据大小 + // 6. Verify chunk data size if (chunkData.byteLength !== chunkMeta.chunkSize) { - postLogToBackend( - `[DEBUG] ⚠️ Chunk size mismatch - meta: ${chunkMeta.chunkSize}, actual: ${chunkData.byteLength}` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ⚠️ Chunk size mismatch - meta: ${chunkMeta.chunkSize}, actual: ${chunkData.byteLength}` + ); + } } return { chunkMeta, chunkData }; } catch (error) { - postLogToBackend(`[DEBUG] ❌ Failed to parse embedded packet: ${error}`); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ❌ Failed to parse embedded packet: ${error}` + ); + } return null; } } @@ -570,14 +604,16 @@ class FileReceiver { this.fireError("Error parsing received JSON data", { error }); } } else { - // 🚀 新版本:处理融合数据包 - 彻底解决Firefox乱序问题 + // 🚀 New Version: Process embedded packets - Completely solve Firefox out-of-order issue const arrayBuffer = await this.convertToArrayBuffer(data); if (arrayBuffer) { if (!this.activeFileReception) { - postLogToBackend( - `[Firefox Debug] ERROR: Received file chunk but no active file reception!` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ERROR: Received file chunk but no active file reception!` + ); + } this.fireError( "Received a file chunk without an active file reception.", { peerId } @@ -585,12 +621,14 @@ class FileReceiver { return; } - // 🚀 统一处理:所有数据都作为融合数据包处理 + // 🚀 Unified processing: All data is processed as embedded packets await this.handleEmbeddedChunkPacket(arrayBuffer); } else { - postLogToBackend( - `[Firefox Debug] ERROR: Failed to convert binary data to ArrayBuffer` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ERROR: Failed to convert binary data to ArrayBuffer` + ); + } this.fireError("Received unsupported binary data format", { dataType: Object.prototype.toString.call(data), peerId, @@ -653,12 +691,10 @@ class FileReceiver { } } - // endregion - // region File and Folder Processing /** - * 🚀 新版本:处理融合数据包 + * 🚀 New Version: Process embedded packets */ private async handleEmbeddedChunkPacket( arrayBuffer: ArrayBuffer @@ -672,21 +708,25 @@ class FileReceiver { const { chunkMeta, chunkData } = parsed; const reception = this.activeFileReception!; - // 验证fileId匹配 + // Verify fileId match if (chunkMeta.fileId !== reception.meta.fileId) { - postLogToBackend( - `[DEBUG] ⚠️ FileId mismatch - expected: ${reception.meta.fileId}, got: ${chunkMeta.fileId}` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ⚠️ FileId mismatch - expected: ${reception.meta.fileId}, got: ${chunkMeta.fileId}` + ); + } return; } - // 更新预期 chunks 数量(可能与初始预估不同) + // Update expected chunks count (may differ from initial estimate) if (chunkMeta.totalChunks !== reception.expectedChunksCount) { - postLogToBackend( - `[DEBUG] ⚠️ Chunk count adjustment - expected: ${reception.expectedChunksCount}, actual: ${chunkMeta.totalChunks}` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ⚠️ Chunk count adjustment - expected: ${reception.expectedChunksCount}, actual: ${chunkMeta.totalChunks}` + ); + } reception.expectedChunksCount = chunkMeta.totalChunks; - // 调整chunks数组大小 + // Adjust chunks array size if (reception.chunks.length < chunkMeta.totalChunks) { const newChunks = new Array(chunkMeta.totalChunks).fill(null); reception.chunks.forEach((chunk, index) => { @@ -696,33 +736,35 @@ class FileReceiver { } } - // 按序号存储chunk + // Store chunk by index const chunkIndex = chunkMeta.chunkIndex; if (chunkIndex >= 0 && chunkIndex < reception.chunks.length) { reception.chunks[chunkIndex] = chunkData; reception.chunkSequenceMap.set(chunkIndex, true); reception.receivedChunksCount++; - // 更新进度 + // Update progress this.updateProgress(chunkData.byteLength); if (reception.sequencedWriter) { - // 🚀 使用严格按序写入管理器 + // 🚀 Use strict sequential write management await reception.sequencedWriter.writeChunk(chunkIndex, chunkData); } } else { - postLogToBackend( - `[DEBUG] ❌ Invalid chunk index - ${chunkIndex}, expected 0-${ - reception.chunks.length - 1 - }` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ❌ Invalid chunk index - ${chunkIndex}, expected 0-${ + reception.chunks.length - 1 + }` + ); + } } await this.checkAndAutoFinalize(); } /** - * 🚀 新版本:统一的自动完成检查 - 支持融合数据包和旧格式 + * 🚀 Unified auto-complete check */ private async checkAndAutoFinalize(): Promise { if (!this.activeFileReception) return; @@ -731,13 +773,13 @@ class FileReceiver { const receivedChunks = reception.receivedChunksCount; const expectedChunks = reception.expectedChunksCount; - // 计算当前实际接收的总大小 + // Calculate current actual total received size const currentTotalSize = reception.chunks.reduce((sum, chunk) => { return sum + (chunk instanceof ArrayBuffer ? chunk.byteLength : 0); }, 0); const expectedSize = reception.meta.size; - // 🚀 统一完整性检查:按序接收模式 + // 🚀 Unified integrity check: sequential reception mode let sequencedCount = 0; for (let i = 0; i < expectedChunks; i++) { if (reception.chunks[i] instanceof ArrayBuffer) { @@ -749,7 +791,7 @@ class FileReceiver { const sizeComplete = currentTotalSize >= expectedSize; const isDataComplete = isSequencedComplete && sizeComplete; - // 防止重复finalize + // Prevent duplicate finalize if (reception.isFinalized) { return; } @@ -765,7 +807,9 @@ class FileReceiver { } this.activeFileReception = null; } catch (error) { - postLogToBackend(`[DEBUG] ❌ Auto-finalize ERROR: ${error}`); + if (developmentEnv === "true") { + postLogToBackend(`[DEBUG] ❌ Auto-finalize ERROR: ${error}`); + } if (reception.completionNotifier) { reception.completionNotifier.reject(error); } @@ -844,16 +888,18 @@ class FileReceiver { this.activeFileReception.fileHandle = fileHandle; this.activeFileReception.writeStream = writeStream; - // 🚀 创建严格按序写入管理器 - const startChunkIndex = Math.floor(offset / 65536); // 计算起始块索引 + // 🚀 Create a strictly sequential write manager + const startChunkIndex = Math.floor(offset / 65536); // Calculate starting chunk index this.activeFileReception.sequencedWriter = new SequencedDiskWriter( writeStream, startChunkIndex ); - postLogToBackend( - `[DEBUG] 📢 SEQUENCED_WRITER created - startIndex: ${startChunkIndex}, offset: ${offset}` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] 📢 SEQUENCED_WRITER created - startIndex: ${startChunkIndex}, offset: ${offset}` + ); + } } catch (err) { this.fireError("Failed to create file on disk", { err, @@ -888,20 +934,24 @@ class FileReceiver { if (!reception?.writeStream || !reception.fileHandle) return; try { - // 🚀 先关闭严格按序写入管理器(刷新所有缓冲) + // 🚀 First close the strict sequential writing manager (flush all buffers) if (reception.sequencedWriter) { await reception.sequencedWriter.close(); const status = reception.sequencedWriter.getBufferStatus(); - postLogToBackend( - `[DEBUG] 💾 SEQUENCED_WRITER closed - totalWritten: ${status.totalWritten}, finalQueue: ${status.queueSize}` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] 💾 SEQUENCED_WRITER closed - totalWritten: ${status.totalWritten}, finalQueue: ${status.queueSize}` + ); + } reception.sequencedWriter = null; } - // 然后关闭文件流 + // Then close the file stream await reception.writeStream.close(); - postLogToBackend(`[DEBUG] ✅ LARGE_FILE finalized successfully`); + if (developmentEnv === "true") { + postLogToBackend(`[DEBUG] ✅ LARGE_FILE finalized successfully`); + } } catch (error) { this.fireError("Error finalizing large file", { error }); } @@ -913,7 +963,7 @@ class FileReceiver { const reception = this.activeFileReception; if (!reception) return; - // 🚀 简化版:验证按序接收的数据 + // 🚀 Simplified: Verify sequentially received data let totalChunkSize = 0; let validChunks = 0; @@ -924,15 +974,17 @@ class FileReceiver { } }); - // 最终验证 + // Final verification const sizeDifference = reception.meta.size - totalChunkSize; if (sizeDifference !== 0) { - postLogToBackend( - `[DEBUG] ❌ SIZE_MISMATCH - missing: ${sizeDifference} bytes` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] ❌ SIZE_MISMATCH - missing: ${sizeDifference} bytes` + ); + } } - // 创建文件 + // Create file const fileBlob = new Blob( reception.chunks.filter( (chunk) => chunk instanceof ArrayBuffer @@ -959,7 +1011,7 @@ class FileReceiver { storeUpdated = true; } - // 发送完成确认 + // Send completion confirmation this.sendFileReceiveComplete( reception.meta.fileId, totalChunkSize, @@ -967,12 +1019,10 @@ class FileReceiver { storeUpdated ); } - // endregion - // region Communication /** - * 发送文件接收完成确认 - 新的接收端主导流程 + * Send file reception completion confirmation - New receiver-dominated process */ private sendFileReceiveComplete( fileId: string, @@ -997,7 +1047,7 @@ class FileReceiver { } /** - * 发送文件夹接收完成确认 + * Send folder reception completion confirmation */ private sendFolderReceiveComplete( folderName: string, @@ -1018,9 +1068,11 @@ class FileReceiver { this.peerId ); - postLogToBackend( - `[Firefox Debug] 📤 Sent folderReceiveComplete - folderName: ${folderName}, completedFiles: ${completedFileIds.length}, allStoreUpdated: ${allStoreUpdated}, success: ${success}` - ); + if (developmentEnv === "true") { + postLogToBackend( + `[DEBUG] 📤 Sent folderReceiveComplete - folderName: ${folderName}, completedFiles: ${completedFileIds.length}, allStoreUpdated: ${allStoreUpdated}, success: ${success}` + ); + } } // endregion @@ -1030,7 +1082,7 @@ class FileReceiver { "log", "Attempting to gracefully close sequenced writer on page unload." ); - // 🚀 先关闭严格按序写入管理器 + // 🚀 First close the strict sequential writing manager this.activeFileReception.sequencedWriter.close().catch((err) => { this.log( "error", diff --git a/frontend/lib/fileSender.ts b/frontend/lib/fileSender.ts index 53cb14b..57b9779 100644 --- a/frontend/lib/fileSender.ts +++ b/frontend/lib/fileSender.ts @@ -1,18 +1,18 @@ -// 🚀 新流程 - 接收端主导的文件传输 -// 重构后的FileSender - 使用模块化架构 +// 🚀 New process - Receiver-initiated file transfer +// Refactored FileSender - Using modular architecture import WebRTC_Initiator from "./webrtc_Initiator"; import { CustomFile } from "@/types/webrtc"; import { FileTransferOrchestrator } from "./transfer/FileTransferOrchestrator"; /** - * 🚀 FileSender - 向后兼容包装层 + * 🚀 FileSender - Backward compatible wrapper layer * - * 重构说明: - * - 原875行单体类重构为模块化架构 - * - 内部使用FileTransferOrchestrator统一编排 - * - 保持100%向后兼容的公共API - * - 获得高性能文件读取、智能背压控制等优势 + * Refactoring notes: + * - Original 875-line monolithic class refactored into modular architecture + * - Internally uses FileTransferOrchestrator for unified orchestration + * - Maintains 100% backward compatible public API + * - Gains advantages such as high-performance file reading and intelligent backpressure control */ class FileSender { private orchestrator: FileTransferOrchestrator; @@ -22,8 +22,6 @@ class FileSender { console.log("[FileSender] ✅ Initialized with modular architecture"); } - // ===== 向后兼容的公共API ===== - public sendFileMeta(files: CustomFile[], peerId?: string): void { return this.orchestrator.sendFileMeta(files, peerId); } @@ -39,8 +37,6 @@ class FileSender { return this.orchestrator.setProgressCallback(callback, peerId); } - // ===== 新增API ===== - public getTransferStats(peerId?: string) { return this.orchestrator.getTransferStats(peerId); } diff --git a/frontend/lib/fileUtils.ts b/frontend/lib/fileUtils.ts index 3316815..4d93b9e 100644 --- a/frontend/lib/fileUtils.ts +++ b/frontend/lib/fileUtils.ts @@ -27,7 +27,7 @@ export const downloadAs = async ( file: Blob | File, saveName: string ): Promise => { - // 检查文件是否为空 + // Check if file is empty if (file.size === 0) { postLogToBackend( `[Firefox Debug] CRITICAL ERROR: downloadAs received a file with 0 size! This is the root cause of the 0-byte download issue.` diff --git a/frontend/lib/transfer/FileTransferOrchestrator.ts b/frontend/lib/transfer/FileTransferOrchestrator.ts index a7ee094..a33e717 100644 --- a/frontend/lib/transfer/FileTransferOrchestrator.ts +++ b/frontend/lib/transfer/FileTransferOrchestrator.ts @@ -16,8 +16,8 @@ import WebRTC_Initiator from "../webrtc_Initiator"; import { postLogToBackend } from "@/app/config/api"; const developmentEnv = process.env.NEXT_PUBLIC_development!; /** - * 🚀 文件传输编排器 - * 整合所有组件,提供统一的文件传输服务 + * 🚀 File transfer orchestrator + * Integrates all components to provide unified file transfer services */ export class FileTransferOrchestrator implements MessageHandlerDelegate { private stateManager: StateManager; @@ -26,7 +26,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { private progressTracker: ProgressTracker; constructor(private webrtcConnection: WebRTC_Initiator) { - // 初始化所有组件 + // Initialize all components this.stateManager = new StateManager(); this.networkTransmitter = new NetworkTransmitter( webrtcConnection, @@ -35,19 +35,19 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { this.progressTracker = new ProgressTracker(this.stateManager); this.messageHandler = new MessageHandler(this.stateManager, this); - // 设置数据处理器 + // Set up data handler this.setupDataHandler(); this.log("log", "FileTransferOrchestrator initialized"); } - // ===== 公共API - 简化的接口 ===== + // ===== Public API - Simplified interface ===== /** - * 🎯 发送文件元数据 + * 🎯 Send file metadata */ public sendFileMeta(files: CustomFile[], peerId?: string): void { - // 记录属于文件夹的文件大小,用于进度计算 + // Record file sizes belonging to folders for progress calculation files.forEach((file) => { if (file.folderName) { const fileId = generateFileId(file); @@ -55,7 +55,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { } }); - // 循环发送所有文件的元数据 + // Loop to send metadata for all files const peers = peerId ? [peerId] : Array.from(this.webrtcConnection.peerConnections.keys()); @@ -80,7 +80,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { } /** - * 🎯 发送字符串内容 + * 🎯 Send string content */ public async sendString(content: string, peerId: string): Promise { const chunkSize = TransferConfig.FILE_CONFIG.CHUNK_SIZE; @@ -90,7 +90,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { chunks.push(content.slice(i, i + chunkSize)); } - // 首先发送元数据 + // First send metadata await this.networkTransmitter.sendWithBackpressure( JSON.stringify({ type: "stringMetadata", @@ -99,7 +99,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { peerId ); - // 逐块发送,使用背压控制 + // Send chunks one by one using backpressure control for (let i = 0; i < chunks.length; i++) { const data = JSON.stringify({ type: "string", @@ -118,16 +118,16 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { } /** - * 🎯 设置进度回调 + * 🎯 Set progress callback */ public setProgressCallback(callback: ProgressCallback, peerId: string): void { this.progressTracker.setProgressCallback(callback, peerId); } - // ===== MessageHandlerDelegate 实现 ===== + // ===== MessageHandlerDelegate Implementation ===== /** - * 📄 处理文件请求(来自MessageHandler的委托) + * 📄 Handle file request (delegated from MessageHandler) */ async handleFileRequest(request: FileRequest, peerId: string): Promise { const file = this.stateManager.getPendingFile(request.fileId); @@ -145,7 +145,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { } /** - * 📝 日志记录(来自MessageHandler的委托) + * 📝 Logging (delegated from MessageHandler) */ public log( level: "log" | "warn" | "error", @@ -156,10 +156,10 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { console[level](prefix, message, context || ""); } - // ===== 内部编排方法 ===== + // ===== Internal Orchestration Methods ===== /** - * 🎯 发送单个文件 + * 🎯 Send single file */ private async sendSingleFile( file: CustomFile, @@ -174,7 +174,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { return; } - // 初始化发送状态 + // Initialize sending state this.stateManager.updatePeerState(peerId, { isSending: true, currentFolderName: file.folderName, @@ -183,7 +183,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { isReading: false, }); - // 初始化进度统计 + // Initialize progress statistics const currentSent = this.stateManager.getFileBytesSent(peerId, fileId); this.stateManager.updateFileBytesSent(peerId, fileId, offset - currentSent); @@ -200,7 +200,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { } /** - * 🚀 处理发送队列 - 使用StreamingFileReader + * 🚀 Process send queue - Using StreamingFileReader */ private async processSendQueue( file: CustomFile, @@ -210,7 +210,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { const peerState = this.stateManager.getPeerState(peerId); const transferStartTime = performance.now(); - // 1. 初始化流式文件读取器 + // 1. Initialize streaming file reader const streamReader = new StreamingFileReader( file, peerState.readOffset || 0 @@ -234,20 +234,20 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { let totalProgressTime = 0; let lastProgressTime = performance.now(); - // 2. 流式处理:逐个获取64KB网络块并发送 + // 2. Stream processing: Get 64KB network chunks one by one and send while (peerState.isSending) { - // 获取下一个网络块 + // Get next network chunk const readStartTime = performance.now(); const chunkInfo = await streamReader.getNextNetworkChunk(); const readTime = performance.now() - readStartTime; totalReadTime += readTime; - // 检查是否已完成 + // Check if completed if (chunkInfo.chunk === null) { break; } - // 构建嵌入式元数据 + // Build embedded metadata const embeddedMeta: EmbeddedChunkMeta = { chunkIndex: chunkInfo.chunkIndex, totalChunks: chunkInfo.totalChunks, @@ -257,7 +257,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { fileId, }; - // 发送带嵌入元数据的网络块 + // Send network chunk with embedded metadata let sendSuccessful = false; const sendStartTime = performance.now(); try { @@ -280,7 +280,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { const sendTime = performance.now() - sendStartTime; totalSendTime += sendTime; - // 更新状态和进度 + // Update state and progress if (sendSuccessful) { this.stateManager.updatePeerState(peerId, { readOffset: chunkInfo.fileOffset + chunkInfo.chunk.byteLength, @@ -300,7 +300,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { networkChunkIndex++; - // 检查是否为最后一块 + // Check if it's the last chunk if (chunkInfo.isLastChunk) { break; } @@ -329,13 +329,13 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { }); throw error; } finally { - // 清理资源 + // Clean up resources streamReader.cleanup(); } } /** - * ⏳ 等待传输完成确认 + * ⏳ Wait for transfer completion confirmation */ private async waitForTransferComplete(peerId: string): Promise { const peerState = this.stateManager.getPeerState(peerId); @@ -345,7 +345,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { } /** - * 📋 获取文件元数据 + * 📋 Get file metadata */ private getFileMeta(file: CustomFile): fileMetadata { const fileId = generateFileId(file); @@ -361,7 +361,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { } /** - * ❌ 中止文件发送 + * ❌ Abort file sending */ private abortFileSend(fileId: string, peerId: string): void { this.log("warn", `Aborting file send for ${fileId} to ${peerId}`); @@ -369,7 +369,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { } /** - * 🔧 设置数据处理器 + * 🔧 Set up data handler */ private setupDataHandler(): void { this.webrtcConnection.onDataReceived = (data, peerId) => { @@ -385,7 +385,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { } /** - * 🔥 错误处理 + * 🔥 Error handling */ private fireError(message: string, context?: Record) { this.webrtcConnection.fireError(message, { @@ -394,10 +394,10 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { }); } - // ===== 状态查询和调试 ===== + // ===== State Query and Debugging ===== /** - * 📊 获取传输统计信息 + * 📊 Get transfer statistics */ public getTransferStats(peerId?: string) { const stats = { @@ -414,7 +414,7 @@ export class FileTransferOrchestrator implements MessageHandlerDelegate { } /** - * 🧹 清理所有资源 + * 🧹 Clean up all resources */ public cleanup(): void { this.stateManager.cleanup(); diff --git a/frontend/lib/transfer/MessageHandler.ts b/frontend/lib/transfer/MessageHandler.ts index 41b93dc..5efda0d 100644 --- a/frontend/lib/transfer/MessageHandler.ts +++ b/frontend/lib/transfer/MessageHandler.ts @@ -8,7 +8,7 @@ import { StateManager } from "./StateManager"; import { postLogToBackend } from "@/app/config/api"; const developmentEnv = process.env.NEXT_PUBLIC_development!; /** - * 🚀 消息处理接口 - 与主编排器通信 + * 🚀 Message handling interface - Communicate with main orchestrator */ export interface MessageHandlerDelegate { handleFileRequest(request: FileRequest, peerId: string): Promise; @@ -20,8 +20,8 @@ export interface MessageHandlerDelegate { } /** - * 🚀 消息处理器 - * 负责WebRTC消息的路由和处理逻辑 + * 🚀 Message handler + * Responsible for WebRTC message routing and processing logic */ export class MessageHandler { constructor( @@ -30,10 +30,10 @@ export class MessageHandler { ) {} /** - * 🎯 处理接收到的信令消息 + * 🎯 Handle received signaling message */ handleSignalingMessage(message: WebRTCMessage, peerId: string): void { - // 删除频繁的消息接收日志 + // Delete frequent message reception logs switch (message.type) { case "fileRequest": @@ -57,7 +57,7 @@ export class MessageHandler { } /** - * 📄 处理文件请求消息 + * 📄 Handle file request message */ private async handleFileRequest( request: FileRequest, @@ -70,10 +70,10 @@ export class MessageHandler { `Handling file request for ${request.fileId} from ${peerId} with offset ${offset}` ); - // Firefox兼容性修复:添加稍长延迟确保接收端完全准备好 + // Firefox compatibility fix: Add slightly longer delay to ensure receiver is fully ready await new Promise((resolve) => setTimeout(resolve, 10)); - // 委托给主编排器处理具体的文件传输 + // Delegate to main orchestrator for specific file transfer try { await this.delegate.handleFileRequest(request, peerId); } catch (error) { @@ -86,24 +86,24 @@ export class MessageHandler { } /** - * ✅ 处理文件接收完成确认消息 + * ✅ Handle file receive completion confirmation message */ private handleFileReceiveComplete( message: FileReceiveComplete, peerId: string ): void { - // 清理发送状态 + // Clean up sending state this.stateManager.updatePeerState(peerId, { isSending: false }); - // 获取peer状态以触发进度回调 + // Get peer state to trigger progress callback const peerState = this.stateManager.getPeerState(peerId); - // 触发单文件100%进度(只有非文件夹情况) + // Trigger single file 100% progress (only for non-folder cases) if (!peerState.currentFolderName) { - // 删除频繁的进度日志 + // Delete frequent progress logs peerState.progressCallback?.(message.fileId, 1, 0); } else { - // 删除频繁的文件夹进度日志 + // Delete frequent folder progress logs } this.delegate.log("log", `File reception confirmed by peer ${peerId}`, { @@ -114,7 +114,7 @@ export class MessageHandler { } /** - * 📁 处理文件夹接收完成确认消息 + * 📁 Handle folder receive completion confirmation message */ private handleFolderReceiveComplete( message: FolderReceiveComplete, @@ -126,10 +126,10 @@ export class MessageHandler { ); } - // 获取peer状态以触发进度回调 + // Get peer state to trigger progress callback const peerState = this.stateManager.getPeerState(peerId); - // 触发文件夹100%进度 + // Trigger folder 100% progress const folderMeta = this.stateManager.getFolderMeta(message.folderName); if (folderMeta) { postLogToBackend( @@ -155,21 +155,21 @@ export class MessageHandler { } /** - * 📊 获取消息处理统计信息 + * 📊 Get message handling statistics */ public getMessageStats(): { handledMessages: number; lastMessageTime: number | null; } { - // 这里可以添加消息统计逻辑,如果需要的话 + // Message statistics logic can be added here if needed return { - handledMessages: 0, // TODO: 实现消息计数 - lastMessageTime: null, // TODO: 记录最后消息时间 + handledMessages: 0, // TODO: Implement message counting + lastMessageTime: null, // TODO: Record last message time }; } /** - * 🧹 清理资源 + * 🧹 Clean up resources */ public cleanup(): void { postLogToBackend("[DEBUG] 🧹 MessageHandler cleaned up"); diff --git a/frontend/lib/transfer/NetworkTransmitter.ts b/frontend/lib/transfer/NetworkTransmitter.ts index 875f523..7b0da2c 100644 --- a/frontend/lib/transfer/NetworkTransmitter.ts +++ b/frontend/lib/transfer/NetworkTransmitter.ts @@ -4,8 +4,8 @@ import WebRTC_Initiator from "../webrtc_Initiator"; import { postLogToBackend } from "@/app/config/api"; const developmentEnv = process.env.NEXT_PUBLIC_development!; /** - * 🚀 网络传输器 - 简化版 - * 使用WebRTC原生bufferedAmountLowThreshold进行背压控制 + * 🚀 Network transmitter - Simplified version + * Uses WebRTC native bufferedAmountLowThreshold for backpressure control */ export class NetworkTransmitter { constructor( @@ -14,7 +14,7 @@ export class NetworkTransmitter { ) {} /** - * 🎯 发送带序号的融合数据包 + * 🎯 Send embedded chunk packet with sequence number */ async sendEmbeddedChunk( chunkData: ArrayBuffer, @@ -22,16 +22,16 @@ export class NetworkTransmitter { peerId: string ): Promise { try { - // 1. 构建融合数据包 + // 1. Build fused data packet const embeddedPacket = this.createEmbeddedChunkPacket( chunkData, metadata ); - // 2. 发送完整的融合数据包(不可分片) + // 2. Send complete fused data packet (no fragmentation) await this.sendSingleData(embeddedPacket, peerId); - // 关键节点日志(仅开发环境) + // Key node logs (development environment only) if ( developmentEnv === "true" && @@ -58,26 +58,26 @@ export class NetworkTransmitter { } /** - * 🚀 构建融合元数据的数据包 + * 🚀 Build data packet with embedded metadata */ private createEmbeddedChunkPacket( chunkData: ArrayBuffer, chunkMeta: EmbeddedChunkMeta ): ArrayBuffer { - // 1. 将元数据序列化为JSON + // 1. Serialize metadata to JSON const metaJson = JSON.stringify(chunkMeta); const metaBytes = new TextEncoder().encode(metaJson); - // 2. 元数据长度(4字节) + // 2. Metadata length (4 bytes) const metaLengthBuffer = new ArrayBuffer(4); const metaLengthView = new Uint32Array(metaLengthBuffer); metaLengthView[0] = metaBytes.length; - // 3. 构建最终的融合数据包 + // 3. Build final fused packet const totalLength = 4 + metaBytes.length + chunkData.byteLength; const finalPacket = new Uint8Array(totalLength); - // 拼接: [4字节长度] + [元数据] + [原始chunk数据] + // Concatenate: [4-byte length] + [metadata] + [original chunk data] finalPacket.set(new Uint8Array(metaLengthBuffer), 0); finalPacket.set(metaBytes, 4); finalPacket.set(new Uint8Array(chunkData), 4 + metaBytes.length); @@ -86,7 +86,7 @@ export class NetworkTransmitter { } /** - * 🚀 发送单个数据包(禁止分片) + * 🚀 Send single data packet (no fragmentation) */ private async sendSingleData( data: string | ArrayBuffer, @@ -97,10 +97,10 @@ export class NetworkTransmitter { throw new Error("Data channel not found"); } - // 简化背压控制 + // Simplified backpressure control await this.simpleBufferControl(dataChannel, peerId); - // 直接发送,不分片 + // Send directly, no fragmentation const sendResult = this.webrtcConnection.sendData(data, peerId); if (!sendResult) { @@ -114,21 +114,21 @@ export class NetworkTransmitter { } /** - * 🎯 原生背压控制 - 使用WebRTC标准机制 + * 🎯 Native backpressure control - Using WebRTC standard mechanism */ private async simpleBufferControl( dataChannel: RTCDataChannel, peerId: string ): Promise { - const maxBuffer = 3 * 1024 * 1024; // 3MB最大缓冲 - const lowThreshold = 512 * 1024; // 512KB低阈值 + const maxBuffer = 3 * 1024 * 1024; // 3MB maximum buffer + const lowThreshold = 512 * 1024; // 512KB low threshold - // 设置原生低阈值 + // Set native low threshold if (dataChannel.bufferedAmountLowThreshold !== lowThreshold) { dataChannel.bufferedAmountLowThreshold = lowThreshold; } - // 如果缓冲区超过最大值,等待降到低阈值 + // If buffer exceeds maximum, wait until it drops to low threshold if (dataChannel.bufferedAmount > maxBuffer) { const startTime = performance.now(); const initialBuffered = dataChannel.bufferedAmount; @@ -140,14 +140,14 @@ export class NetworkTransmitter { }; dataChannel.addEventListener("bufferedamountlow", onLow); - // 添加超时保护,避免无限等待 + // Add timeout protection to avoid infinite waiting setTimeout(() => { dataChannel.removeEventListener("bufferedamountlow", onLow); resolve(); - }, 5000); // 5秒超时 + }, 5000); // 5 second timeout }); - // 仅在开发环境输出背压日志 + // Only output backpressure logs in development environment if (developmentEnv === "true") { const waitTime = performance.now() - startTime; postLogToBackend( @@ -162,7 +162,7 @@ export class NetworkTransmitter { } /** - * 🚀 发送带背压控制的数据 + * 🚀 Send data with backpressure control */ async sendWithBackpressure( data: string | ArrayBuffer, @@ -174,7 +174,7 @@ export class NetworkTransmitter { } try { - // 对于ArrayBuffer,如果超过64KB,需要分片发送(修复sendData failed) + // For ArrayBuffer, if larger than 64KB, needs to be fragmented (fix sendData failed) if (data instanceof ArrayBuffer) { await this.sendLargeArrayBuffer(data, peerId); } else { @@ -190,7 +190,7 @@ export class NetworkTransmitter { } /** - * 🚀 发送大型ArrayBuffer(分片处理) + * 🚀 Send large ArrayBuffer (fragmentation processing) */ private async sendLargeArrayBuffer( data: ArrayBuffer, @@ -199,13 +199,13 @@ export class NetworkTransmitter { const networkChunkSize = 65536; // 64KB const totalSize = data.byteLength; - // 如果数据小于64KB,直接发送 + // If data is less than 64KB, send directly if (totalSize <= networkChunkSize) { await this.sendSingleData(data, peerId); return; } - // 大块数据分片发送 + // Fragment large data for sending let offset = 0; let fragmentIndex = 0; @@ -213,7 +213,7 @@ export class NetworkTransmitter { const chunkSize = Math.min(networkChunkSize, totalSize - offset); const chunk = data.slice(offset, offset + chunkSize); - // 发送分片 + // Send fragment await this.sendSingleData(chunk, peerId); offset += chunkSize; @@ -222,7 +222,7 @@ export class NetworkTransmitter { } /** - * 📊 获取传输统计信息 + * 📊 Get transmission statistics */ public getTransmissionStats(peerId: string) { const dataChannel = this.webrtcConnection.dataChannels.get(peerId); @@ -236,7 +236,7 @@ export class NetworkTransmitter { } /** - * 🧹 清理资源 + * 🧹 Clean up resources */ public cleanup(): void { if (developmentEnv === "true") { diff --git a/frontend/lib/transfer/ProgressTracker.ts b/frontend/lib/transfer/ProgressTracker.ts index cc1007c..14cd129 100644 --- a/frontend/lib/transfer/ProgressTracker.ts +++ b/frontend/lib/transfer/ProgressTracker.ts @@ -3,7 +3,7 @@ import { StateManager } from "./StateManager"; import { postLogToBackend } from "@/app/config/api"; /** - * 🚀 进度回调类型定义 + * 🚀 Progress callback type definition */ export type ProgressCallback = ( fileId: string, @@ -12,8 +12,8 @@ export type ProgressCallback = ( ) => void; /** - * 🚀 进度跟踪器 - * 负责文件和文件夹的进度计算、速度统计、回调触发 + * 🚀 Progress tracker + * Responsible for file and folder progress calculation, speed statistics, and callback triggering */ export class ProgressTracker { private speedCalculator = new SpeedCalculator(); @@ -21,7 +21,7 @@ export class ProgressTracker { constructor(private stateManager: StateManager) {} /** - * 🎯 更新文件传输进度 + * 🎯 Update file transfer progress */ async updateFileProgress( byteLength: number, @@ -33,20 +33,20 @@ export class ProgressTracker { const peerState = this.stateManager.getPeerState(peerId); if (!peerState) return; - // 重要修复:只有成功发送的数据才更新统计 + // Important fix: Only update statistics for successfully sent data if (!wasActuallySent) { return; } - // 更新文件已发送字节数 + // Update file sent bytes this.stateManager.updateFileBytesSent(peerId, fileId, byteLength); - // 计算进度ID和统计数据 + // Calculate progress ID and statistics let progressFileId = fileId; let currentBytes = this.stateManager.getFileBytesSent(peerId, fileId); let totalSize = fileSize; - // 如果文件属于文件夹,重新计算文件夹进度 + // If file belongs to a folder, recalculate folder progress if (peerState.currentFolderName) { const folderName = peerState.currentFolderName; const folderMeta = this.stateManager.getFolderMeta(folderName); @@ -54,24 +54,24 @@ export class ProgressTracker { progressFileId = folderName; totalSize = folderMeta?.totalSize || 0; - // 重新计算文件夹进度(从其所有文件的进度总和) - // 这对于断点续传更加健壮和正确 + // Recalculate folder progress (sum of progress from all its files) + // This is more robust and correct for resume downloads currentBytes = this.stateManager.getFolderBytesSent(peerId, folderName); - // 删除频繁的文件夹进度日志 + // Delete frequent folder progress logs } - // 更新速度计算器 + // Update speed calculator this.speedCalculator.updateSendSpeed(peerId, currentBytes); const speed = this.speedCalculator.getSendSpeed(peerId); const progress = totalSize > 0 ? currentBytes / totalSize : 0; - // 触发进度回调 + // Trigger progress callback this.triggerProgressCallback(peerId, progressFileId, progress, speed); } /** - * 🎯 更新文件夹传输进度 + * 🎯 Update folder transfer progress */ async updateFolderProgress( folderName: string, @@ -84,7 +84,7 @@ export class ProgressTracker { return; } - // 计算文件夹总进度 + // Calculate total folder progress let totalSentBytes = 0; folderMeta.fileIds.forEach((fileId) => { totalSentBytes += this.stateManager.getFileBytesSent(peerId, fileId); @@ -94,7 +94,7 @@ export class ProgressTracker { folderMeta.totalSize > 0 ? totalSentBytes / folderMeta.totalSize : 0; const speed = this.speedCalculator.getSendSpeed(peerId); - // 触发文件夹进度回调 + // Trigger folder progress callback this.triggerProgressCallback(peerId, folderName, progress, speed); postLogToBackend( @@ -107,14 +107,14 @@ export class ProgressTracker { } /** - * 🎯 设置进度回调函数 + * 🎯 Set progress callback function */ setProgressCallback(callback: ProgressCallback, peerId: string): void { this.stateManager.updatePeerState(peerId, { progressCallback: callback }); } /** - * 🎯 触发进度回调 + * 🎯 Trigger progress callback */ private triggerProgressCallback( peerId: string, @@ -135,14 +135,14 @@ export class ProgressTracker { } /** - * 🎯 计算当前传输速度 + * 🎯 Calculate current transfer speed */ getCurrentSpeed(peerId: string): number { return this.speedCalculator.getSendSpeed(peerId); } /** - * 🎯 完成文件传输进度(设置为100%) + * 🎯 Complete file transfer progress (set to 100%) */ completeFileProgress(fileId: string, peerId: string): void { this.triggerProgressCallback(peerId, fileId, 1.0, 0); @@ -151,7 +151,7 @@ export class ProgressTracker { } /** - * 🎯 完成文件夹传输进度(设置为100%) + * 🎯 Complete folder transfer progress (set to 100%) */ completeFolderProgress(folderName: string, peerId: string): void { this.triggerProgressCallback(peerId, folderName, 1.0, 0); @@ -160,13 +160,13 @@ export class ProgressTracker { } /** - * 📊 获取详细的进度统计信息 + * 📊 Get detailed progress statistics */ getProgressStats(peerId: string) { const peerState = this.stateManager.getPeerState(peerId); const currentSpeed = this.getCurrentSpeed(peerId); - // 计算总的已发送字节数 + // Calculate total sent bytes let totalBytesSent = 0; Object.values(peerState.totalBytesSent).forEach((bytes) => { totalBytesSent += bytes; @@ -184,7 +184,7 @@ export class ProgressTracker { } /** - * 📊 获取文件夹的详细进度信息 + * 📊 Get detailed folder progress information */ getFolderProgressDetails(folderName: string, peerId: string) { const folderMeta = this.stateManager.getFolderMeta(folderName); @@ -198,8 +198,8 @@ export class ProgressTracker { folderMeta.fileIds.forEach((fileId) => { const sent = this.stateManager.getFileBytesSent(peerId, fileId); - // 注意:这里需要从pendingFiles获取文件大小,暂时使用0 - const total = 0; // TODO: 需要从StateManager获取文件大小 + // Note: Need to get file size from pendingFiles, temporarily using 0 + const total = 0; // TODO: Need to get file size from StateManager totalSent += sent; fileProgresses[fileId] = { @@ -221,10 +221,10 @@ export class ProgressTracker { } /** - * 🧹 清理进度跟踪资源 + * 🧹 Clean up progress tracking resources */ cleanup(): void { - // SpeedCalculator 内部会自动清理过期数据 + // SpeedCalculator internally automatically cleans up expired data postLogToBackend("[DEBUG] 🧹 ProgressTracker cleaned up"); } } diff --git a/frontend/lib/transfer/StateManager.ts b/frontend/lib/transfer/StateManager.ts index bdc2f2a..6473f5c 100644 --- a/frontend/lib/transfer/StateManager.ts +++ b/frontend/lib/transfer/StateManager.ts @@ -1,19 +1,19 @@ import { PeerState, CustomFile, FolderMeta } from "@/types/webrtc"; -// 简化版不再依赖TransferConfig的复杂配置 +// Simplified version no longer depends on TransferConfig's complex configuration /** - * 🚀 网络性能监控指标接口 + * 🚀 Network performance monitoring metrics interface */ export interface NetworkPerformanceMetrics { - avgClearingRate: number; // 平均网络清理速度 KB/s - optimalThreshold: number; // 动态优化的阈值 - avgWaitTime: number; // 平均等待时间 - sampleCount: number; // 样本计数 + avgClearingRate: number; // Average network clearing speed KB/s + optimalThreshold: number; // Dynamically optimized threshold + avgWaitTime: number; // Average waiting time + sampleCount: number; // Sample count } /** - * 🚀 状态管理类 - * 集中管理所有传输相关的状态数据 + * 🚀 State management class + * Centrally manages all transfer-related state data */ export class StateManager { private peerStates = new Map(); @@ -21,10 +21,10 @@ export class StateManager { private pendingFolderMeta: Record = {}; private networkPerformance = new Map(); - // ===== Peer状态管理 ===== + // ===== Peer state management ===== /** - * 获取或创建peer状态 + * Get or create peer state */ public getPeerState(peerId: string): PeerState { if (!this.peerStates.has(peerId)) { @@ -42,7 +42,7 @@ export class StateManager { } /** - * 更新peer状态 + * Update peer state */ public updatePeerState(peerId: string, updates: Partial): void { const currentState = this.getPeerState(peerId); @@ -50,7 +50,7 @@ export class StateManager { } /** - * 重置peer状态(传输完成或出错时) + * Reset peer state (when transfer is complete or error occurs) */ public resetPeerState(peerId: string): void { const peerState = this.getPeerState(peerId); @@ -58,51 +58,51 @@ export class StateManager { peerState.readOffset = 0; peerState.bufferQueue = []; peerState.isReading = false; - // 保留 currentFolderName, totalBytesSent, progressCallback + // Preserve currentFolderName, totalBytesSent, progressCallback } /** - * 删除peer状态(peer断开连接时) + * Remove peer state (when peer disconnects) */ public removePeerState(peerId: string): void { this.peerStates.delete(peerId); this.networkPerformance.delete(peerId); } - // ===== 文件管理 ===== + // ===== File management ===== /** - * 添加待发送文件 + * Add pending file to send */ public addPendingFile(fileId: string, file: CustomFile): void { this.pendingFiles.set(fileId, file); } /** - * 获取待发送文件 + * Get pending file to send */ public getPendingFile(fileId: string): CustomFile | undefined { return this.pendingFiles.get(fileId); } /** - * 删除待发送文件 + * Remove pending file to send */ public removePendingFile(fileId: string): void { this.pendingFiles.delete(fileId); } /** - * 获取所有待发送文件 + * Get all pending files to send */ public getAllPendingFiles(): Map { return new Map(this.pendingFiles); } - // ===== 文件夹元数据管理 ===== + // ===== Folder metadata management ===== /** - * 添加或更新文件夹元数据 + * Add or update folder metadata */ public addFileToFolder(folderName: string, fileId: string, fileSize: number): void { if (!this.pendingFolderMeta[folderName]) { @@ -117,22 +117,22 @@ export class StateManager { } /** - * 获取文件夹元数据 + * Get folder metadata */ public getFolderMeta(folderName: string): FolderMeta | undefined { return this.pendingFolderMeta[folderName]; } /** - * 获取所有文件夹元数据 + * Get all folder metadata */ public getAllFolderMeta(): Record { return { ...this.pendingFolderMeta }; } - // ===== 进度跟踪相关状态 ===== + // ===== Progress tracking related state ===== /** - * 更新文件发送字节数 + * Update file sent bytes */ public updateFileBytesSent(peerId: string, fileId: string, bytes: number): void { const peerState = this.getPeerState(peerId); @@ -143,7 +143,7 @@ export class StateManager { } /** - * 获取文件已发送字节数 + * Get file sent bytes */ public getFileBytesSent(peerId: string, fileId: string): number { const peerState = this.peerStates.get(peerId); @@ -151,7 +151,7 @@ export class StateManager { } /** - * 计算文件夹总发送字节数 + * Calculate folder total sent bytes */ public getFolderBytesSent(peerId: string, folderName: string): number { const folderMeta = this.getFolderMeta(folderName); @@ -167,10 +167,10 @@ export class StateManager { return totalSent; } - // ===== 清理和重置 ===== + // ===== Cleanup and reset ===== /** - * 清理所有状态(系统重置时) + * Clean up all states (when system resets) */ public cleanup(): void { this.peerStates.clear(); @@ -180,7 +180,7 @@ export class StateManager { } /** - * 获取状态统计信息(调试用) + * Get state statistics (for debugging) */ public getStateStats() { return { diff --git a/frontend/lib/transfer/StreamingFileReader.ts b/frontend/lib/transfer/StreamingFileReader.ts index 170388b..087c536 100644 --- a/frontend/lib/transfer/StreamingFileReader.ts +++ b/frontend/lib/transfer/StreamingFileReader.ts @@ -3,7 +3,7 @@ import { TransferConfig } from "./TransferConfig"; import { postLogToBackend } from "@/app/config/api"; const developmentEnv = process.env.NEXT_PUBLIC_development!; /** - * 🚀 网络块信息接口 + * 🚀 Network chunk interface */ export interface NetworkChunk { chunk: ArrayBuffer | null; @@ -14,33 +14,33 @@ export interface NetworkChunk { } /** - * 🚀 高性能流式文件读取器 - * 使用双层缓冲架构:大块批量读取 + 小块网络发送 - * 解决文件读取性能瓶颈问题 + * 🚀 High-performance streaming file reader + * Uses a two-layer buffering architecture: large batch reading + small network chunk sending + * Solves file reading performance bottleneck issues */ export class StreamingFileReader { - // 配置参数 + // Configuration parameters private readonly BATCH_SIZE = TransferConfig.FILE_CONFIG.CHUNK_SIZE * - TransferConfig.FILE_CONFIG.BATCH_SIZE; // 32MB批次 + TransferConfig.FILE_CONFIG.BATCH_SIZE; // 32MB batches private readonly NETWORK_CHUNK_SIZE = - TransferConfig.FILE_CONFIG.NETWORK_CHUNK_SIZE; // 64KB网络块 - private readonly CHUNKS_PER_BATCH = this.BATCH_SIZE / this.NETWORK_CHUNK_SIZE; // 512块 + TransferConfig.FILE_CONFIG.NETWORK_CHUNK_SIZE; // 64KB network chunks + private readonly CHUNKS_PER_BATCH = this.BATCH_SIZE / this.NETWORK_CHUNK_SIZE; // 512 chunks - // 文件状态 + // File state private file: File; private fileReader: FileReader; private totalFileSize: number; - // 批次缓冲状态 - private currentBatch: ArrayBuffer | null = null; // 当前32MB批次数据 - private currentBatchStartOffset = 0; // 当前批次在文件中的起始位置 - private currentChunkIndexInBatch = 0; // 当前网络块在批次中的索引 + // Batch buffering state + private currentBatch: ArrayBuffer | null = null; // Current 32MB batch data + private currentBatchStartOffset = 0; // Starting position of current batch in file + private currentChunkIndexInBatch = 0; // Index of current network chunk in batch - // 全局状态 - private totalFileOffset = 0; // 当前在整个文件中的位置 + // Global state + private totalFileOffset = 0; // Current position in the entire file private isFinished = false; - private isReading = false; // 防止并发读取 + private isReading = false; // Prevent concurrent reading constructor(file: CustomFile, startOffset: number = 0) { this.file = file; @@ -60,15 +60,15 @@ export class StreamingFileReader { } /** - * 🎯 核心方法:获取下一个64KB网络块 + * 🎯 Core method: Get next 64KB network chunk */ async getNextNetworkChunk(): Promise { - // 1. 检查是否需要加载新批次 + // 1. Check if new batch needs to be loaded if (this.needsNewBatch()) { await this.loadNextBatch(); } - // 2. 检查是否已到文件末尾 + // 2. Check if end of file has been reached if (this.isFinished || !this.currentBatch) { return { chunk: null, @@ -79,15 +79,15 @@ export class StreamingFileReader { }; } - // 3. 从当前批次中切片出64KB网络块 + // 3. Slice 64KB network chunk from current batch const networkChunk = this.sliceNetworkChunkFromBatch(); const globalChunkIndex = this.calculateGlobalChunkIndex(); const isLast = this.isLastNetworkChunk(networkChunk); - // 4. 更新状态 + // 4. Update state this.updateChunkState(networkChunk); - // 删除频繁的chunk进度日志 + // Delete frequent chunk progress logs return { chunk: networkChunk, @@ -99,18 +99,18 @@ export class StreamingFileReader { } /** - * 🔍 判断是否需要加载新批次 + * 🔍 Determine if new batch needs to be loaded */ private needsNewBatch(): boolean { return ( - this.currentBatch === null || // 还未加载任何批次 - this.currentChunkIndexInBatch >= this.CHUNKS_PER_BATCH || // 当前批次用完 - this.isCurrentBatchEmpty() // 当前批次已无数据 + this.currentBatch === null || // No batch loaded yet + this.currentChunkIndexInBatch >= this.CHUNKS_PER_BATCH || // Current batch exhausted + this.isCurrentBatchEmpty() // Current batch has no data ); } /** - * 🔍 判断当前批次是否为空 + * 🔍 Check if current batch is empty */ private isCurrentBatchEmpty(): boolean { if (!this.currentBatch) return true; @@ -120,11 +120,11 @@ export class StreamingFileReader { } /** - * 📥 加载下一个32MB批次到内存 + * 📥 Load next 32MB batch into memory */ private async loadNextBatch(): Promise { if (this.isReading) { - // 防止并发读取 + // Prevent concurrent reading while (this.isReading) { await new Promise((resolve) => setTimeout(resolve, 10)); } @@ -135,10 +135,10 @@ export class StreamingFileReader { const startTime = performance.now(); try { - // 1. 清理旧批次内存 + // 1. Clean up old batch memory this.currentBatch = null; - // 2. 计算本次要读取的大小 + // 2. Calculate size to read this time const remainingFileSize = this.totalFileSize - this.totalFileOffset; const batchSize = Math.min(this.BATCH_SIZE, remainingFileSize); @@ -147,7 +147,7 @@ export class StreamingFileReader { return; } - // 3. 执行大块文件读取 + // 3. Perform large chunk file reading const sliceStartTime = performance.now(); const fileSlice = this.file.slice( this.totalFileOffset, @@ -155,7 +155,7 @@ export class StreamingFileReader { ); const sliceTime = performance.now() - sliceStartTime; - // 4. 异步读取文件数据 + // 4. Asynchronously read file data const readStartTime = performance.now(); this.currentBatch = await this.readFileSlice(fileSlice); const readTime = performance.now() - readStartTime; @@ -163,7 +163,7 @@ export class StreamingFileReader { this.currentBatchStartOffset = this.totalFileOffset; this.currentChunkIndexInBatch = 0; - // 仅在开发环境输出批次读取日志 + // Only output batch reading logs in development environment if (developmentEnv === "true") { const totalTime = performance.now() - startTime; const speedMBps = batchSize / 1024 / 1024 / (totalTime / 1000); @@ -186,7 +186,7 @@ export class StreamingFileReader { } /** - * 📄 执行文件读取操作 + * 📄 Perform file reading operation */ private async readFileSlice(fileSlice: Blob): Promise { return new Promise((resolve, reject) => { @@ -214,7 +214,7 @@ export class StreamingFileReader { } /** - * ✂️ 从32MB批次中切片出64KB网络块 + * ✂️ Slice 64KB network chunk from 32MB batch */ private sliceNetworkChunkFromBatch(): ArrayBuffer { if (!this.currentBatch) { @@ -235,12 +235,12 @@ export class StreamingFileReader { chunkStartInBatch + chunkSize ); - // 删除频繁的slice日志,只在需要时输出 + // Delete frequent slice logs, only output when needed return networkChunk; } /** - * 📊 计算全局网络块索引 + * 📊 Calculate global network chunk index */ private calculateGlobalChunkIndex(): number { const batchesBefore = Math.floor( @@ -251,34 +251,34 @@ export class StreamingFileReader { } /** - * 📈 计算总网络块数量 + * 📈 Calculate total network chunk count */ private calculateTotalNetworkChunks(): number { return Math.ceil(this.totalFileSize / this.NETWORK_CHUNK_SIZE); } /** - * ⏭️ 更新当前处理状态 + * ⏭️ Update current processing state */ private updateChunkState(chunk: ArrayBuffer): void { this.currentChunkIndexInBatch++; this.totalFileOffset += chunk.byteLength; - // 检查是否到达文件末尾 + // Check if end of file has been reached if (this.totalFileOffset >= this.totalFileSize) { this.isFinished = true; } } /** - * 🏁 判断是否为最后一个网络块 + * 🏁 Check if this is the last network chunk */ private isLastNetworkChunk(chunk: ArrayBuffer): boolean { return this.totalFileOffset + chunk.byteLength >= this.totalFileSize; } /** - * 📊 获取读取进度信息 + * 📊 Get reading progress information */ public getProgress(): { readBytes: number; @@ -317,7 +317,7 @@ export class StreamingFileReader { } /** - * 🔄 重置读取器状态(用于重新开始读取) + * 🔄 Reset reader state (for restarting reading) */ public reset(startOffset: number = 0): void { this.totalFileOffset = startOffset; @@ -332,22 +332,22 @@ export class StreamingFileReader { } /** - * 🧹 清理和释放资源 + * 🧹 Cleanup and release resources */ public cleanup(): void { - // 中断正在进行的文件读取 + // Abort ongoing file reading if (this.isReading) { this.fileReader.abort(); } - // 清理内存 + // Clean up memory this.currentBatch = null; this.isFinished = true; this.isReading = false; } /** - * 🔍 获取调试信息 + * 🔍 Get debug information */ public getDebugInfo() { return { diff --git a/frontend/lib/transfer/TransferConfig.ts b/frontend/lib/transfer/TransferConfig.ts index 08b2443..4faeff3 100644 --- a/frontend/lib/transfer/TransferConfig.ts +++ b/frontend/lib/transfer/TransferConfig.ts @@ -1,12 +1,12 @@ /** - * 🚀 传输配置管理类 - * 集中管理所有文件传输相关的配置参数 + * 🚀 Transfer configuration management class + * Centrally manages all file transfer related configuration parameters */ export class TransferConfig { - // 文件I/O相关配置 + // File I/O related configuration static readonly FILE_CONFIG = { - CHUNK_SIZE: 4194304, // 4MB - 文件读取块大小,减少FileReader调用次数 - BATCH_SIZE: 8, // 8个chunk批处理 - 32MB批处理提升性能 - NETWORK_CHUNK_SIZE: 65536, // 64KB - WebRTC安全发送大小,修复sendData failed + CHUNK_SIZE: 4194304, // 4MB - File reading chunk size, reduces FileReader calls + BATCH_SIZE: 8, // 8 chunks batch processing - 32MB batch processing improves performance + NETWORK_CHUNK_SIZE: 65536, // 64KB - WebRTC safe sending size, fixes sendData failed } as const; } diff --git a/frontend/lib/transfer/index.ts b/frontend/lib/transfer/index.ts index ba798c9..41d0b22 100644 --- a/frontend/lib/transfer/index.ts +++ b/frontend/lib/transfer/index.ts @@ -1,35 +1,35 @@ /** - * 🚀 文件传输模块统一导出 - * 提供模块化的文件传输服务 + * 🚀 File transfer module unified export + * Provides modular file transfer services */ -// 配置管理 +// Configuration management export { TransferConfig } from "./TransferConfig"; -// 状态管理 +// State management export { StateManager } from "./StateManager"; export type { NetworkPerformanceMetrics } from "./StateManager"; -// 高性能文件读取 +// High-performance file reading export { StreamingFileReader } from "./StreamingFileReader"; export type { NetworkChunk } from "./StreamingFileReader"; -// 网络传输 +// Network transmission export { NetworkTransmitter } from "./NetworkTransmitter"; -// 消息处理 +// Message handling export { MessageHandler } from "./MessageHandler"; export type { MessageHandlerDelegate } from "./MessageHandler"; -// 进度跟踪 +// Progress tracking export { ProgressTracker } from "./ProgressTracker"; export type { ProgressCallback } from "./ProgressTracker"; -// 主编排器 +// Main orchestrator export { FileTransferOrchestrator } from "./FileTransferOrchestrator"; /** - * 🎯 便捷创建函数 - 快速初始化文件传输服务 + * 🎯 Convenience creation function - Quick initialization of file transfer services */ import WebRTC_Initiator from "../webrtc_Initiator"; import { FileTransferOrchestrator } from "./FileTransferOrchestrator"; diff --git a/frontend/lib/webrtc_base.ts b/frontend/lib/webrtc_base.ts index dece3c1..790daa0 100644 --- a/frontend/lib/webrtc_base.ts +++ b/frontend/lib/webrtc_base.ts @@ -352,7 +352,7 @@ export default class BaseWebRTC { }; dataChannel.onmessage = (event) => { - // 增强的数据类型检测 - 支持Firefox的多种二进制数据格式 + // Enhanced data type detection - supports multiple binary data formats in Firefox let dataType = "Unknown"; let dataSize = 0; @@ -372,7 +372,7 @@ export default class BaseWebRTC { dataType = "TypedArray"; dataSize = event.data.byteLength; } else { - // 详细的未知类型调试信息 + // Detailed unknown type debug information dataType = `Unknown(${Object.prototype.toString.call(event.data)})`; dataSize = event.data?.length || event.data?.size || event.data?.byteLength || 0; @@ -477,7 +477,7 @@ export default class BaseWebRTC { const dataChannel = this.dataChannels.get(peerId); if (dataChannel?.readyState === "open") { try { - // Firefox兼容性调试:记录发送详细信息 + // Firefox compatibility debugging: Log sending details const dataType = typeof data === "string" ? "string"