处理windows老版本频繁开始文件读写流导致的错误

This commit is contained in:
kura 2026-05-07 18:21:16 +08:00
parent b90c695207
commit cb40ea89a7
2 changed files with 57 additions and 29 deletions

View File

@ -80,6 +80,9 @@ export class FileTransfer {
this.pausePromise = undefined; this.pausePromise = undefined;
this.pauseResolve = undefined; this.pauseResolve = undefined;
this.aborted = false; this.aborted = false;
this.closeReceiveWritable().catch((error) => {
console.error("close receive writable error", error);
});
this.receiveQueue = Promise.resolve(); this.receiveQueue = Promise.resolve();
} }
@ -119,6 +122,8 @@ export class FileTransfer {
public totalSize: number = 0; public totalSize: number = 0;
private fileBuffer: ArrayBuffer | null = null; private fileBuffer: ArrayBuffer | null = null;
private receiveQueue: Promise<void> = Promise.resolve(); private receiveQueue: Promise<void> = Promise.resolve();
private receiveWritable: FileSystemWritableFileStream | null = null;
private receiveWritablePath: string = "";
private async waitForWritable(targetBufferedAmount = MAX_BUFFERED_AMOUNT) { private async waitForWritable(targetBufferedAmount = MAX_BUFFERED_AMOUNT) {
const dc = this.conn.dataChannel; const dc = this.conn.dataChannel;
if (!dc) { if (!dc) {
@ -139,6 +144,36 @@ export class FileTransfer {
await new Promise((resolve) => setTimeout(resolve, 10)); await new Promise((resolve) => setTimeout(resolve, 10));
} }
} }
private async getReceiveWritable(path: string) {
if (this.receiveWritable && this.receiveWritablePath === path) {
return this.receiveWritable;
}
if (this.receiveWritable) {
await this.receiveWritable.close();
this.receiveWritable = null;
}
let dir = this.file.fileDirHandler as FileSystemDirectoryHandle;
let name = path;
if (name.split("/").length > 1) {
const dirPath = name.split("/").slice(0, -1).join("/");
name = name.split("/").slice(-1).join("");
dir = await this.file.createPath(dirPath);
}
const fileHandle = await dir.getFileHandle(name, { create: true });
this.receiveWritable = await fileHandle.createWritable();
this.receiveWritablePath = path;
return this.receiveWritable;
}
private async closeReceiveWritable() {
if (!this.receiveWritable) return;
const writable = this.receiveWritable;
this.receiveWritable = null;
this.receiveWritablePath = "";
await writable.close();
}
// 发送文件 (流水线模式 - 受限深度,防止撑爆SCTP缓冲区) // 发送文件 (流水线模式 - 受限深度,防止撑爆SCTP缓冲区)
public async sendFile(savePath: string = ""): Promise<boolean> { public async sendFile(savePath: string = ""): Promise<boolean> {
try { try {
@ -182,7 +217,6 @@ export class FileTransfer {
data: this.fileData, data: this.fileData,
}, },
this.conn, this.conn,
true,
); );
this.offset = end; this.offset = end;
@ -261,11 +295,15 @@ export class FileTransfer {
); );
} else { } else {
const path = fData.savePath + "/" + fData.name; const path = fData.savePath + "/" + fData.name;
await this.file.createFile( const writable = await this.getReceiveWritable(path);
path, await writable.write({
fData.chunkData.buffer, type: "write",
fData.chunkData.offset, position: fData.chunkData.offset,
); data: fData.chunkData.buffer,
});
if (this.status === TransferStatus.COMPLETED) {
await this.closeReceiveWritable();
}
// if (this.status == TransferStatus.COMPLETED) { // if (this.status == TransferStatus.COMPLETED) {
// await this.file.renameFile(path, fData.savePath + '/' + fData.name); // await this.file.renameFile(path, fData.savePath + '/' + fData.name);
// } // }
@ -304,6 +342,9 @@ export class FileTransfer {
// 取消传输 // 取消传输
public abort() { public abort() {
this.aborted = true; this.aborted = true;
this.closeReceiveWritable().catch((error) => {
console.error("close receive writable error", error);
});
this.resume(); // 恢复暂停的传输以便能够正确退出 this.resume(); // 恢复暂停的传输以便能够正确退出
} }
@ -400,22 +441,16 @@ export class TransferTask {
this.updateProgress(); this.updateProgress();
} }
public updateProgress(): TransferProgress { public updateProgress(): TransferProgress {
if (this.status == TransferStatus.COMPLETED) {
this.progress.updateTime = Date.now();
return this.progress;
}
const totalSize = this.fileData.chunkData?.totalSize || this.fileData.size; const totalSize = this.fileData.chunkData?.totalSize || this.fileData.size;
const transferredSize = const transferredSize =
this.fileData.chunkData?.offset || this.fileData.size; this.fileData.chunkData
? this.fileData.chunkData.offset + this.fileData.chunkData.buffer.byteLength
: this.fileData.size;
const speed = this.fileData.chunkData const speed = this.fileData.chunkData
? ((transferredSize + this.fileData.chunkData.buffer.byteLength) / ? (transferredSize / (Date.now() - this.startTime)) * 1000
(Date.now() - this.startTime)) *
1000
: (this.fileData.size / (Date.now() - this.startTime)) * 1000; : (this.fileData.size / (Date.now() - this.startTime)) * 1000;
const percent = this.fileData.chunkData const percent = this.fileData.chunkData
? ((transferredSize + this.fileData.chunkData.buffer.byteLength) / ? (transferredSize / totalSize) * 100
totalSize) *
100
: (transferredSize / totalSize) * 100; : (transferredSize / totalSize) * 100;
this.status = this.status =
transferredSize >= totalSize ? TransferStatus.COMPLETED : this.status; transferredSize >= totalSize ? TransferStatus.COMPLETED : this.status;

View File

@ -16,7 +16,7 @@ import {
} from "./common"; } from "./common";
// 发送超时时间(毫秒) // 发送超时时间(毫秒)
const SEND_TIMEOUT = 10000; const SEND_TIMEOUT = 30000;
class Peer extends EventTarget { class Peer extends EventTarget {
peer: PeerJs; peer: PeerJs;
@ -600,23 +600,16 @@ class Peer extends EventTarget {
case MessageType.push_file_chunk: case MessageType.push_file_chunk:
resData.type = MessageType.response_push_file_chunk; resData.type = MessageType.response_push_file_chunk;
resData.data = "ok"; resData.data = "ok";
// 先回复确认,再异步处理文件I/O,避免阻塞消息循环
let fData = remoteD as FileData; let fData = remoteD as FileData;
if (fData.preView) { if (fData.preView) {
fileMgrInstance.remoteRootFile await fileMgrInstance.remoteRootFile
.getFileInfo(fData.path) .getFileInfo(fData.path)
.getTransfer(conn) .getTransfer(conn)
.receiveFile(fData, true) .receiveFile(fData, true);
.catch((err) => {
console.error("receiveFile preview error", err);
});
} else { } else {
(await fileMgrInstance.getRootFile()) await (await fileMgrInstance.getRootFile())
.getTransfer(conn) .getTransfer(conn)
.receiveFile(fData) .receiveFile(fData);
.catch((err) => {
console.error("receiveFile error", err);
});
} }
break; break;
case MessageType.push_file_complete: case MessageType.push_file_complete: