parallel-file-uploader
Version:
高性能并行文件上传工具,支持大文件分片上传、断点续传、Web Worker多线程处理
221 lines (217 loc) • 6.72 kB
JavaScript
/**
* Worker管理器
* 管理Web Worker线程池,处理数据分片处理
*/
export class WorkerManager {
constructor() {
this.workerPool = [];
this.workerBusy = new Map();
this.messageHandlers = new Map();
this.initializeWorkers();
}
/**
* 创建Worker实例
*/
createWorker() {
// 在浏览器环境中,创建内联Worker
if (typeof window !== 'undefined' && typeof Blob !== 'undefined') {
try {
// 创建包含worker代码的内联Worker
const workerCode = `
// Web Worker实现文件分片处理
// 专门负责数据处理,不进行网络请求
// 适配Worker环境
const ctx = self;
ctx.onmessage = async function (e) {
const message = e.data;
// 处理所有包含文件数据的消息
if (message.fileId && message.chunkInfo) {
const { fileId, chunkInfo } = message;
try {
// 将处理后的数据发送回主线程
ctx.postMessage({
type: 'response',
fileId,
chunkInfo,
processed: true,
result: {
file: chunkInfo.file, // 返回处理后的Blob
partNumber: chunkInfo.partNumber,
partSize: chunkInfo.partSize,
},
});
} catch (error) {
// 发送错误消息
ctx.postMessage({
type: 'error',
fileId,
chunkInfo,
error: error instanceof Error ? error.message : String(error),
});
}
}
};
// 通知主线程Worker已经准备就绪
ctx.postMessage({ type: 'ready' });
`;
const blob = new Blob([workerCode], { type: 'application/javascript' });
const workerUrl = URL.createObjectURL(blob);
const worker = new Worker(workerUrl);
// 清理Blob URL(可选,但是好的实践)
worker.addEventListener('message', () => {
URL.revokeObjectURL(workerUrl);
}, { once: true });
return worker;
}
catch (e) {
console.warn('Failed to create inline worker:', e);
}
}
// 回退方案:尝试加载外部worker文件
try {
// 在开发环境中,尝试加载编译后的文件
return new Worker('./lib/worker.js');
}
catch (e) {
try {
// 生产环境的另一个路径
return new Worker('/lib/worker.js');
}
catch (e2) {
// 最终回退
return new Worker('./worker.js');
}
}
}
/**
* 初始化Worker池
*/
initializeWorkers() {
if (typeof Worker === 'undefined') {
console.warn('Web Workers not supported in this environment');
return;
}
// 在测试环境中跳过Worker初始化
if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'test') {
console.log('Skipping worker initialization in test environment');
return;
}
const workerCount = Math.min(navigator.hardwareConcurrency || 4, 8);
for (let i = 0; i < workerCount; i++) {
try {
const worker = this.createWorker();
const messageHandler = this.createMessageHandler.bind(this);
worker.onmessage = messageHandler;
this.workerPool.push(worker);
this.workerBusy.set(worker, false);
this.messageHandlers.set(worker, messageHandler);
}
catch (error) {
console.error('Failed to create worker:', error);
break;
}
}
console.log(`Initialized ${this.workerPool.length} workers`);
}
/**
* 创建消息处理器
*/
createMessageHandler(event) {
const message = event.data;
const worker = event.target;
// 处理Worker就绪消息
if (message.type === 'ready') {
console.log('Worker ready');
this.workerBusy.set(worker, false);
return;
}
// 标记Worker为可用
this.workerBusy.set(worker, false);
// 触发全局消息处理器
if (this.globalMessageHandler) {
this.globalMessageHandler(event);
}
}
/**
* 设置全局消息处理器
*/
setMessageHandler(handler) {
this.globalMessageHandler = handler;
}
/**
* 获取可用的Worker
*/
getAvailableWorker() {
for (const worker of this.workerPool) {
if (!this.workerBusy.get(worker)) {
return worker;
}
}
return null;
}
/**
* 标记Worker为忙碌
*/
markWorkerBusy(worker) {
this.workerBusy.set(worker, true);
}
/**
* 标记Worker为空闲
*/
markWorkerIdle(worker) {
this.workerBusy.set(worker, false);
}
/**
* 向Worker发送消息
*/
postMessage(worker, message, transferable) {
if (transferable) {
worker.postMessage(message, transferable);
}
else {
worker.postMessage(message);
}
}
/**
* 检查是否有可用的Worker
*/
hasAvailableWorker() {
return this.getAvailableWorker() !== null;
}
/**
* 获取Worker池统计信息
*/
getStats() {
const total = this.workerPool.length;
let busy = 0;
for (const worker of this.workerPool) {
if (this.workerBusy.get(worker)) {
busy++;
}
}
return {
total,
busy,
idle: total - busy
};
}
/**
* 销毁所有Worker
*/
destroy() {
for (const worker of this.workerPool) {
worker.terminate();
}
this.workerPool = [];
this.workerBusy.clear();
this.messageHandlers.clear();
this.globalMessageHandler = undefined;
}
/**
* 🔧 检查是否支持 Web Worker
*/
isSupported() {
return typeof Worker !== 'undefined' && typeof window !== 'undefined';
}
}
//# sourceMappingURL=WorkerManager.js.map