处理windows老版本频繁开始文件读写流导致的错误
This commit is contained in:
parent
b90c695207
commit
cb40ea89a7
@ -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;
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user