处理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.pauseResolve = undefined;
this.aborted = false;
this.closeReceiveWritable().catch((error) => {
console.error("close receive writable error", error);
});
this.receiveQueue = Promise.resolve();
}
@ -119,6 +122,8 @@ export class FileTransfer {
public totalSize: number = 0;
private fileBuffer: ArrayBuffer | null = null;
private receiveQueue: Promise<void> = Promise.resolve();
private receiveWritable: FileSystemWritableFileStream | null = null;
private receiveWritablePath: string = "";
private async waitForWritable(targetBufferedAmount = MAX_BUFFERED_AMOUNT) {
const dc = this.conn.dataChannel;
if (!dc) {
@ -139,6 +144,36 @@ export class FileTransfer {
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缓冲区)
public async sendFile(savePath: string = ""): Promise<boolean> {
try {
@ -182,7 +217,6 @@ export class FileTransfer {
data: this.fileData,
},
this.conn,
true,
);
this.offset = end;
@ -261,11 +295,15 @@ export class FileTransfer {
);
} else {
const path = fData.savePath + "/" + fData.name;
await this.file.createFile(
path,
fData.chunkData.buffer,
fData.chunkData.offset,
);
const writable = await this.getReceiveWritable(path);
await writable.write({
type: "write",
position: fData.chunkData.offset,
data: fData.chunkData.buffer,
});
if (this.status === TransferStatus.COMPLETED) {
await this.closeReceiveWritable();
}
// if (this.status == TransferStatus.COMPLETED) {
// await this.file.renameFile(path, fData.savePath + '/' + fData.name);
// }
@ -304,6 +342,9 @@ export class FileTransfer {
// 取消传输
public abort() {
this.aborted = true;
this.closeReceiveWritable().catch((error) => {
console.error("close receive writable error", error);
});
this.resume(); // 恢复暂停的传输以便能够正确退出
}
@ -400,22 +441,16 @@ export class TransferTask {
this.updateProgress();
}
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 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
? ((transferredSize + this.fileData.chunkData.buffer.byteLength) /
(Date.now() - this.startTime)) *
1000
? (transferredSize / (Date.now() - this.startTime)) * 1000
: (this.fileData.size / (Date.now() - this.startTime)) * 1000;
const percent = this.fileData.chunkData
? ((transferredSize + this.fileData.chunkData.buffer.byteLength) /
totalSize) *
100
? (transferredSize / totalSize) * 100
: (transferredSize / totalSize) * 100;
this.status =
transferredSize >= totalSize ? TransferStatus.COMPLETED : this.status;

View File

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