clear up code

This commit is contained in:
david_bai
2025-09-07 21:21:43 +08:00
parent 3f18002cf0
commit 99c927f5c7
9 changed files with 14 additions and 340 deletions
+1 -3
View File
@@ -122,9 +122,7 @@ export function useFileTransferHandler({
}
// 检查文件是否为有效的Blob
if (fileToDownload instanceof Blob) {
postLogToBackend(`[Firefox Debug] File is a valid Blob object`);
} else {
if (!(fileToDownload instanceof Blob)) {
postLogToBackend(
`[Firefox Debug] WARNING: File is not a Blob object, type: ${typeof fileToDownload}`
);
-111
View File
@@ -16,65 +16,6 @@ export const isChrome = (): boolean => {
);
};
/**
* 检测是否为 Firefox 浏览器
* @returns {boolean} 如果是 Firefox 返回 true,否则返回 false
*/
export const isFirefox = (): boolean => {
return navigator.userAgent.includes("Firefox");
};
/**
* 检测浏览器详细信息
*/
export function detectBrowser(): {
name: string;
version: string;
isFirefox: boolean;
isChrome: boolean;
isSafari: boolean;
isEdge: boolean;
} {
const userAgent = navigator.userAgent;
let name = "Unknown";
let version = "Unknown";
// Firefox检测
if (userAgent.includes("Firefox/")) {
name = "Firefox";
const match = userAgent.match(/Firefox\/(\d+(?:\.\d+)*)/);
if (match) version = match[1];
}
// Chrome检测 (注意:需要在Edge之前检测,因为Edge也包含Chrome字符串)
else if (userAgent.includes("Chrome/") && !userAgent.includes("Edg/")) {
name = "Chrome";
const match = userAgent.match(/Chrome\/(\d+(?:\.\d+)*)/);
if (match) version = match[1];
}
// Edge检测
else if (userAgent.includes("Edg/")) {
name = "Edge";
const match = userAgent.match(/Edg\/(\d+(?:\.\d+)*)/);
if (match) version = match[1];
}
// Safari检测
else if (userAgent.includes("Safari/") && !userAgent.includes("Chrome/")) {
name = "Safari";
const match = userAgent.match(/Version\/(\d+(?:\.\d+)*)/);
if (match) version = match[1];
}
return {
name,
version,
isFirefox: name === "Firefox",
isChrome: name === "Chrome",
isSafari: name === "Safari",
isEdge: name === "Edge",
};
}
/**
* 检测是否支持程序化下载
* Chrome 支持长时间传输后的自动下载,其他浏览器可能有限制
@@ -83,55 +24,3 @@ export function detectBrowser(): {
export const supportsAutoDownload = (): boolean => {
return isChrome();
};
/**
* 获取Firefox特定的WebRTC配置
*/
export function getFirefoxWebRTCConfig() {
return {
// Firefox可能需要更大的缓冲区阈值
bufferThreshold: 65536 * 4, // 256KB instead of 64KB
// Firefox可能需要更长的延迟
requestDelay: 15, // 15ms instead of 10ms
// Firefox对ArrayBuffer处理的特殊配置
binaryType: "arraybuffer" as BinaryType,
};
}
/**
* 为Firefox优化DataChannel配置
*/
export function getDataChannelConfig(browserName?: string): RTCDataChannelInit {
const isFirefoxBrowser = browserName === "Firefox" || isFirefox();
if (isFirefoxBrowser) {
return {
ordered: true,
// Firefox特定优化:更大的maxPacketLifeTime可能有助于数据传输
maxRetransmits: 3,
};
}
return {
ordered: true,
};
}
/**
* 记录浏览器兼容性信息
*/
export function logBrowserCompatibility() {
const browser = detectBrowser();
const message = `[Browser Compatibility] Browser: ${browser.name} ${browser.version}, isFirefox: ${browser.isFirefox}, userAgent: ${navigator.userAgent}`;
console.log(message);
// 动态导入以避免循环依赖
import("@/app/config/api")
.then(({ postLogToBackend }) => {
postLogToBackend(message);
})
.catch(console.error);
return browser;
}
-56
View File
@@ -359,10 +359,6 @@ class FileReceiver {
expectedChunksCount: expectedChunksCount,
chunkSequenceMap: new Map<number, boolean>(),
};
postLogToBackend(
`[DEBUG] 🚀 FILE_INIT - ${fileInfo.name}, size: ${fileInfo.size}, chunks: ${expectedChunksCount}`
);
});
if (shouldSaveToDisk) {
@@ -373,9 +369,6 @@ class FileReceiver {
if (this.peerId) {
this.webrtcConnection.sendData(JSON.stringify(request), this.peerId);
this.log("log", "Sent fileRequest", { request });
// 调试日志:记录发送完成
postLogToBackend(`[DEBUG] 📤 FILE_REQUEST sent`);
} else {
postLogToBackend(
`[Firefox Debug] ERROR: Cannot send fileRequest - no peerId available!`
@@ -547,10 +540,6 @@ class FileReceiver {
);
}
postLogToBackend(
`[DEBUG] 📦 PARSED embedded packet - chunkIndex: ${chunkMeta.chunkIndex}/${chunkMeta.totalChunks}, chunkSize: ${chunkData.byteLength}, isLast: ${chunkMeta.isLastChunk}`
);
return { chunkMeta, chunkData };
} catch (error) {
postLogToBackend(`[DEBUG] ❌ Failed to parse embedded packet: ${error}`);
@@ -714,18 +703,12 @@ class FileReceiver {
reception.chunkSequenceMap.set(chunkIndex, true);
reception.receivedChunksCount++;
postLogToBackend(
`[DEBUG] ✓ SEQUENCED chunk #${chunkIndex}/${chunkMeta.totalChunks} stored - size: ${chunkData.byteLength}, isLast: ${chunkMeta.isLastChunk}`
);
// 更新进度
this.updateProgress(chunkData.byteLength);
if (reception.sequencedWriter) {
// 🚀 使用严格按序写入管理器
await reception.sequencedWriter.writeChunk(chunkIndex, chunkData);
} else {
postLogToBackend(`[DEBUG] ❌ Error - no sequencedWriter available`);
}
} else {
postLogToBackend(
@@ -766,27 +749,12 @@ class FileReceiver {
const sizeComplete = currentTotalSize >= expectedSize;
const isDataComplete = isSequencedComplete && sizeComplete;
// 更频繁的调试信息只在接近完成时显示
if (
receivedChunks % 10 === 0 ||
receivedChunks >= expectedChunks - 5 ||
isDataComplete
) {
postLogToBackend(
`[DEBUG] 🔄 SEQUENCED progress - received: ${sequencedCount}/${expectedChunks}, total: ${currentTotalSize}/${expectedSize}, complete: ${isDataComplete}`
);
}
// 防止重复finalize
if (reception.isFinalized) {
return;
}
if (isDataComplete) {
postLogToBackend(
`[DEBUG] 🎯 TRIGGERING finalize - chunks: ${sequencedCount}/${expectedChunks}, size: ${currentTotalSize}/${expectedSize}`
);
reception.isFinalized = true;
try {
@@ -796,8 +764,6 @@ class FileReceiver {
reception.completionNotifier.resolve();
}
this.activeFileReception = null;
postLogToBackend(`[DEBUG] ✅ Auto-finalize SUCCESS`);
} catch (error) {
postLogToBackend(`[DEBUG] ❌ Auto-finalize ERROR: ${error}`);
if (reception.completionNotifier) {
@@ -947,10 +913,6 @@ class FileReceiver {
const reception = this.activeFileReception;
if (!reception) return;
postLogToBackend(
`[DEBUG] 🔍 FINALIZE START - fileName: ${reception.meta.name}, expectedSize: ${reception.meta.size}, chunksArray: ${reception.chunks.length}`
);
// 🚀 简化版:验证按序接收的数据
let totalChunkSize = 0;
let validChunks = 0;
@@ -962,18 +924,12 @@ class FileReceiver {
}
});
postLogToBackend(
`[DEBUG] 📊 SEQUENCED_SUMMARY - valid: ${validChunks}/${reception.chunks.length}, totalSize: ${totalChunkSize}, expected: ${reception.meta.size}`
);
// 最终验证
const sizeDifference = reception.meta.size - totalChunkSize;
if (sizeDifference !== 0) {
postLogToBackend(
`[DEBUG] ❌ SIZE_MISMATCH - missing: ${sizeDifference} bytes`
);
} else {
postLogToBackend(`[DEBUG] ✅ SIZE_VERIFIED - ${totalChunkSize} bytes`);
}
// 创建文件
@@ -990,12 +946,6 @@ class FileReceiver {
type: reception.meta.fileType,
});
postLogToBackend(
`[DEBUG] 📄 FILE_CREATED - size: ${file.size}, expected: ${
reception.meta.size
}, match: ${file.size === reception.meta.size}`
);
const customFile = Object.assign(file, {
fullName: reception.meta.fullName,
folderName: this.currentFolderName,
@@ -1007,8 +957,6 @@ class FileReceiver {
await Promise.resolve();
await new Promise<void>((resolve) => setTimeout(() => resolve(), 0));
storeUpdated = true;
postLogToBackend(`[DEBUG] ✅ STORE_UPDATED - ${reception.meta.name}`);
}
// 发送完成确认
@@ -1046,10 +994,6 @@ class FileReceiver {
JSON.stringify(completeMessage),
this.peerId
);
postLogToBackend(
`[DEBUG] 📤 SENT fileReceiveComplete - size: ${receivedSize}, chunks: ${receivedChunks}, success: ${success}`
);
}
/**
-63
View File
@@ -114,7 +114,6 @@ class FileSender {
private handleSignalingMessage(message: WebRTCMessage, peerId: string): void {
const peerState = this.getPeerState(peerId);
postLogToBackend(`debug Message:${message.type}`);
switch (message.type) {
case "fileRequest":
this.handleFileRequest(message as FileRequest, peerId);
@@ -151,31 +150,13 @@ class FileSender {
peerId: string
): void {
const peerState = this.getPeerState(peerId);
postLogToBackend(
`[Firefox Debug] 📥 Received fileReceiveComplete - fileId: ${message.fileId}, receivedSize: ${message.receivedSize}, receivedChunks: ${message.receivedChunks}, storeUpdated: ${message.storeUpdated}`
);
// 清理发送状态
peerState.isSending = false;
// 触发单文件100%进度(只有非文件夹情况)
if (!peerState.currentFolderName) {
postLogToBackend(
`[Firefox Debug] 🎯 Setting single file progress to 100% - ${message.fileId}`
);
peerState.progressCallback?.(message.fileId, 1, 0);
} else {
postLogToBackend(
`[Firefox Debug] 📁 File in folder completed, not setting progress yet - ${message.fileId} (folder: ${peerState.currentFolderName})`
);
}
this.log("log", `File reception confirmed by peer ${peerId}`, {
fileId: message.fileId,
receivedSize: message.receivedSize,
storeUpdated: message.storeUpdated,
});
}
/**
@@ -216,15 +197,7 @@ class FileSender {
"log",
`Handling file request for ${request.fileId} from ${peerId} with offset ${offset}`
);
// 🔧 Firefox兼容性修复:添加稍长延迟确保接收端完全准备好
// 根据[[memory:7549586]],这个延迟解决了时序竞态条件
await new Promise((resolve) => setTimeout(resolve, 10));
if (file) {
postLogToBackend(
`[Firefox Debug] Starting file send - fileName: ${file.name}, fileSize: ${file.size}, offset: ${offset}`
);
await this.sendSingleFile(file, peerId, offset);
} else {
this.fireError(`File not found for request`, {
@@ -324,14 +297,6 @@ class FileSender {
try {
await this.processSendQueue(file, peerId);
// 🚀 新流程:不再主动发送fileEnd,等待接收端的fileReceiveComplete确认
postLogToBackend(
`[Firefox Debug] 📤 File sending completed, waiting for receiver confirmation - ${file.name}`
);
// 新流程:让接收端主导完成流程,不再主动发送fileEnd
await this.waitForTransferComplete(peerId); // Wait for receiver's fileReceiveComplete confirmation
} catch (error: any) {
this.fireError(`Error sending file ${file.name}: ${error.message}`, {
@@ -465,10 +430,6 @@ class FileSender {
finalPacket.set(metaBytes, 4);
finalPacket.set(new Uint8Array(chunkData), 4 + metaBytes.length);
postLogToBackend(
`[DEBUG] 📦 EMBEDDED packet created - chunkIndex: ${chunkMeta.chunkIndex}, metaSize: ${metaBytes.length}, chunkSize: ${chunkData.byteLength}, totalSize: ${totalLength}`
);
return finalPacket.buffer;
}
@@ -498,10 +459,6 @@ class FileSender {
// 3. 🔧 关键修复:融合数据包不能被分片,直接发送
await this.sendSingleData(embeddedPacket, peerId);
postLogToBackend(
`[DEBUG] ✓ EMBEDDED chunk #${chunkIndex}/${totalChunks} sent - ${chunkData.byteLength} bytes, packet: ${embeddedPacket.byteLength} bytes, isLast: ${isLastChunk}`
);
}
// New: Send large ArrayBuffer in fragments
@@ -560,14 +517,6 @@ class FileSender {
? data.byteLength
: 0;
// 🚀 关键修复:检查数据包大小,如果超过64KB则需要警告
const maxSafeSize = 64 * 1024; // 64KB
if (data instanceof ArrayBuffer && data.byteLength > maxSafeSize) {
postLogToBackend(
`[DEBUG] ⚠️ Large embedded packet detected: ${data.byteLength} bytes, this may cause issues`
);
}
// Intelligent send control - decide sending strategy based on buffer status
await this.smartBufferControl(dataChannel, peerId);
@@ -837,10 +786,6 @@ class FileSender {
const remainingSize = file.size - fileOffset;
const totalNetworkChunks = Math.ceil(remainingSize / networkChunkSize);
postLogToBackend(
`[DEBUG] 🚀 Starting NETWORK-LEVEL EMBEDDED transfer - file: ${file.name}, totalNetworkChunks: ${totalNetworkChunks}, chunkSize: ${networkChunkSize}, startOffset: ${fileOffset}`
);
// Initialize network performance monitoring
this.initializeNetworkPerformance(peerId);
@@ -877,10 +822,6 @@ class FileSender {
);
sendSuccessful = true;
totalBytesSentInLoop += networkChunk.byteLength;
postLogToBackend(
`[DEBUG] ✓ Network chunk #${networkChunkIndex}/${totalNetworkChunks} sent - ${networkChunk.byteLength} bytes`
);
} catch (error) {
postLogToBackend(
`[Firefox Debug] ❌ Failed to send network chunk #${networkChunkIndex}: ${error}`
@@ -904,10 +845,6 @@ class FileSender {
);
}
}
postLogToBackend(
`[Firefox Debug] 🏁 All network chunks sent (${networkChunkIndex}/${totalNetworkChunks}), waiting for receiver confirmation...`
);
} catch (error: any) {
const errorMessage = `Error in network-level embedded transfer: ${error.message}`;
postLogToBackend(
+7 -17
View File
@@ -27,35 +27,25 @@ export const downloadAs = async (
file: Blob | File,
saveName: string
): Promise<void> => {
// 调试日志:记录downloadAs函数被调用
postLogToBackend(`[Firefox Debug] downloadAs called - fileName: ${saveName}, fileSize: ${file.size}, fileType: ${file.type}`);
// 检查文件是否为空
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.`);
postLogToBackend(
`[Firefox Debug] CRITICAL ERROR: downloadAs received a file with 0 size! This is the root cause of the 0-byte download issue.`
);
}
try {
// 调试日志:记录URL创建过程
postLogToBackend(`[Firefox Debug] Creating object URL for file...`);
const url = URL.createObjectURL(file);
postLogToBackend(`[Firefox Debug] Object URL created successfully: ${url}`);
const a = document.createElement("a");
a.href = url;
a.download = saveName;
// 调试日志:记录DOM操作
postLogToBackend(`[Firefox Debug] Adding anchor element to DOM and triggering click...`);
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
// 调试日志:记录清理过程
postLogToBackend(`[Firefox Debug] Download triggered, cleaning up object URL...`);
URL.revokeObjectURL(url);
postLogToBackend(`[Firefox Debug] downloadAs completed successfully`);
} catch (error) {
postLogToBackend(`[Firefox Debug] ERROR in downloadAs: ${error}`);
throw error;
-33
View File
@@ -124,9 +124,6 @@ class WebRTCService {
};
this.fileReceiver.onFileReceived = async (file) => {
// 调试日志:记录文件接收回调被调用
postLogToBackend(`[Firefox Debug] onFileReceived callback called - fileName: ${file.name}, size: ${file.size}, type: ${file.type}`);
// 🔧 Enhanced fix: Ensure Store state updates are fully synchronized with multiple verifications
const store = useFileTransferStore.getState();
@@ -136,38 +133,8 @@ class WebRTCService {
);
if (!existingFile) {
postLogToBackend(`[Firefox Debug] Adding file to store - no existing file found`);
store.addRetrievedFile(file);
} else {
postLogToBackend(`[Firefox Debug] File already exists in store - skipping duplicate`);
}
// 🔧 Additional ensure: Immediately verify if state update was successful with retry mechanism
let verificationAttempts = 0;
const maxVerificationAttempts = 3;
const verifyFileAdded = () => {
verificationAttempts++;
const updatedStore = useFileTransferStore.getState();
const fileExists = updatedStore.retrievedFiles.some(
(f) => f.name === file.name && f.size === file.size
);
postLogToBackend(`[Firefox Debug] Verification attempt ${verificationAttempts}: fileExists: ${fileExists}, retrievedFiles.length: ${updatedStore.retrievedFiles.length}`);
if (!fileExists && verificationAttempts < maxVerificationAttempts) {
postLogToBackend(`[Firefox Debug] File not found in store, attempting to add again...`);
updatedStore.addRetrievedFile(file);
setTimeout(verifyFileAdded, 10);
} else if (fileExists) {
postLogToBackend(`[Firefox Debug] File successfully added to store!`);
} else {
postLogToBackend(`[Firefox Debug] ERROR: Failed to add file to store after ${maxVerificationAttempts} attempts!`);
}
};
// Perform first verification immediately
verifyFileAdded();
};
}
+6 -26
View File
@@ -1,16 +1,11 @@
// Initiator flow: Join room; receive 'ready' event (this event is triggered by the socket server after a new recipient enters) -> createPeerConnection + createDataChannel -> createAndSendOffer
import BaseWebRTC, { WebRTCConfig } from "./webrtc_base";
import { postLogToBackend } from "@/app/config/api";
import { detectBrowser, getDataChannelConfig, logBrowserCompatibility } from "./browserUtils";
const developmentEnv = process.env.NEXT_PUBLIC_development!; // Development environment
export default class WebRTC_Initiator extends BaseWebRTC {
private browserInfo = detectBrowser();
constructor(config: WebRTCConfig) {
super(config);
// 记录浏览器兼容性信息
logBrowserCompatibility();
this.setupInitiatorSocketListeners();
}
@@ -80,29 +75,14 @@ export default class WebRTC_Initiator extends BaseWebRTC {
return;
}
try {
// 使用浏览器特定的DataChannel配置
const dataChannelConfig = getDataChannelConfig(this.browserInfo.name);
postLogToBackend(
`[Firefox Debug] Creating DataChannel with config - browser: ${this.browserInfo.name}, config: ${JSON.stringify(dataChannelConfig)}`
);
const dataChannel = peerConnection.createDataChannel("dataChannel", dataChannelConfig);
// Firefox特定的缓冲区阈值优化
if (this.browserInfo.isFirefox) {
dataChannel.bufferedAmountLowThreshold = 131072; // 128KB for Firefox
postLogToBackend(`[Firefox Debug] Set Firefox-specific bufferedAmountLowThreshold: 128KB`);
} else {
dataChannel.bufferedAmountLowThreshold = 262144; // 256KB for others
}
const dataChannel = peerConnection.createDataChannel("dataChannel", {
ordered: true,
});
dataChannel.bufferedAmountLowThreshold = 262144; // 256KB for others
this.setupDataChannel(dataChannel, peerId);
this.dataChannels.set(peerId, dataChannel);
postLogToBackend(
`[Firefox Debug] DataChannel created successfully - peer: ${peerId}, label: dataChannel, browser: ${this.browserInfo.name}`
);
} catch (error) {
postLogToBackend(
`[Firefox Debug] Error creating DataChannel - peer: ${peerId}, error: ${error}`
-23
View File
@@ -1,7 +1,6 @@
// Recipient flow: Join room; receive 'offer' event -> createPeerConnection + createDataChannel -> send answer
import BaseWebRTC, { WebRTCConfig } from "./webrtc_base";
import { postLogToBackend } from "@/app/config/api";
import { detectBrowser, logBrowserCompatibility } from "./browserUtils";
const developmentEnv = process.env.NEXT_PUBLIC_development!; // Development environment
interface AnswerPayload {
@@ -9,12 +8,8 @@ interface AnswerPayload {
peerId: string;
}
export default class WebRTC_Recipient extends BaseWebRTC {
private browserInfo = detectBrowser();
constructor(config: WebRTCConfig) {
super(config);
// 记录浏览器兼容性信息
logBrowserCompatibility();
this.setupRecipientSocketListeners();
}
@@ -110,27 +105,9 @@ export default class WebRTC_Recipient extends BaseWebRTC {
return;
}
// 调试日志:记录DataChannel事件监听器设置
postLogToBackend(`[Firefox Debug] Setting ondatachannel listener for peer: ${peerId}, connectionState: ${peerConnection.connectionState}, browser: ${this.browserInfo.name}`);
peerConnection.ondatachannel = (event) => {
postLogToBackend(`[Firefox Debug] Received ondatachannel event - peer: ${peerId}, channel label: ${event.channel.label}, readyState: ${event.channel.readyState}, browser: ${this.browserInfo.name}`);
// Firefox特定的DataChannel配置
if (this.browserInfo.isFirefox) {
// 确保binaryType设置为arraybuffer(虽然这通常是默认值)
try {
// 注意:DataChannel没有binaryType属性,但我们可以在这里做其他Firefox特定的配置
postLogToBackend(`[Firefox Debug] Applying Firefox-specific DataChannel configuration`);
} catch (error) {
postLogToBackend(`[Firefox Debug] Firefox DataChannel configuration error: ${error}`);
}
}
this.setupDataChannel(event.channel, peerId);
this.dataChannels.set(peerId, event.channel);
postLogToBackend(`[Firefox Debug] DataChannel setup completed for peer: ${peerId}, total channels: ${this.dataChannels.size}, browser: ${this.browserInfo.name}`);
};
}
}
-8
View File
@@ -344,15 +344,7 @@ export default class BaseWebRTC {
dataChannel: RTCDataChannel,
peerId: string
): void {
// 调试日志:记录DataChannel设置
postLogToBackend(
`[Firefox Debug] Setting up DataChannel for peer: ${peerId}, label: ${dataChannel.label}, readyState: ${dataChannel.readyState}`
);
dataChannel.onopen = () => {
postLogToBackend(
`[Firefox Debug] DataChannel opened for peer: ${peerId}, readyState: ${dataChannel.readyState}`
);
// this.log('log',`Data channel opened for peer ${peerId}`);
setTimeout(() => {
this.onDataChannelOpen?.(peerId);