837 lines
20 KiB
JavaScript
837 lines
20 KiB
JavaScript
// 媒体文件管理器 - 微信小程序专用
|
||
// 处理图片、视频、音频、文件的上传、下载、预览、缓存等
|
||
|
||
const apiClient = require('../../../utils/api-client.js');
|
||
|
||
/**
|
||
* 媒体文件管理器
|
||
* 功能:
|
||
* 1. 文件选择和上传
|
||
* 2. 媒体文件预览
|
||
* 3. 文件下载和缓存
|
||
* 4. 文件压缩和优化
|
||
* 5. 云存储管理
|
||
* 6. 文件类型检测
|
||
*/
|
||
class MediaManager {
|
||
constructor() {
|
||
this.isInitialized = false;
|
||
|
||
// 媒体配置
|
||
this.mediaConfig = {
|
||
// 图片配置
|
||
image: {
|
||
maxSize: 10 * 1024 * 1024, // 10MB
|
||
maxCount: 9, // 最多选择9张
|
||
quality: 80, // 压缩质量
|
||
formats: ['jpg', 'jpeg', 'png', 'gif', 'webp'],
|
||
compressWidth: 1080 // 压缩宽度
|
||
},
|
||
|
||
// 视频配置
|
||
video: {
|
||
maxSize: 100 * 1024 * 1024, // 100MB
|
||
maxDuration: 300, // 最长5分钟
|
||
formats: ['mp4', 'mov', 'avi'],
|
||
compressQuality: 'medium'
|
||
},
|
||
|
||
// 音频配置
|
||
audio: {
|
||
maxSize: 20 * 1024 * 1024, // 20MB
|
||
maxDuration: 600, // 最长10分钟
|
||
formats: ['mp3', 'wav', 'aac', 'm4a'],
|
||
sampleRate: 16000
|
||
},
|
||
|
||
// 文件配置
|
||
file: {
|
||
maxSize: 50 * 1024 * 1024, // 50MB
|
||
allowedTypes: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'zip', 'rar'],
|
||
maxCount: 5
|
||
},
|
||
|
||
// 缓存配置
|
||
cache: {
|
||
maxSize: 200 * 1024 * 1024, // 200MB
|
||
expireTime: 7 * 24 * 60 * 60 * 1000, // 7天
|
||
cleanupInterval: 24 * 60 * 60 * 1000 // 24小时清理一次
|
||
}
|
||
};
|
||
|
||
// 文件缓存
|
||
this.fileCache = new Map();
|
||
|
||
// 上传队列
|
||
this.uploadQueue = [];
|
||
|
||
// 下载队列
|
||
this.downloadQueue = [];
|
||
|
||
// 当前上传任务
|
||
this.currentUploads = new Map();
|
||
|
||
// 缓存统计
|
||
this.cacheStats = {
|
||
totalSize: 0,
|
||
fileCount: 0,
|
||
lastCleanup: 0
|
||
};
|
||
|
||
this.init();
|
||
}
|
||
|
||
// 初始化媒体管理器
|
||
async init() {
|
||
if (this.isInitialized) return;
|
||
|
||
try {
|
||
// 加载文件缓存信息
|
||
await this.loadCacheInfo();
|
||
|
||
// 检查存储权限
|
||
await this.checkStoragePermission();
|
||
|
||
// 启动缓存清理
|
||
this.startCacheCleanup();
|
||
|
||
this.isInitialized = true;
|
||
|
||
} catch (error) {
|
||
console.error('❌ 媒体文件管理器初始化失败:', error);
|
||
}
|
||
}
|
||
|
||
// 📷 ===== 图片处理 =====
|
||
|
||
// 选择图片
|
||
async chooseImages(options = {}) {
|
||
try {
|
||
const {
|
||
count = this.mediaConfig.image.maxCount,
|
||
sizeType = ['compressed', 'original'],
|
||
sourceType = ['album', 'camera']
|
||
} = options;
|
||
|
||
const result = await new Promise((resolve, reject) => {
|
||
wx.chooseImage({
|
||
count: count,
|
||
sizeType: sizeType,
|
||
sourceType: sourceType,
|
||
success: resolve,
|
||
fail: reject
|
||
});
|
||
});
|
||
|
||
// 验证和处理图片
|
||
const processedImages = await this.processImages(result.tempFilePaths);
|
||
|
||
return {
|
||
success: true,
|
||
images: processedImages
|
||
};
|
||
|
||
} catch (error) {
|
||
console.error('❌ 选择图片失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error.errMsg || '选择图片失败'
|
||
};
|
||
}
|
||
}
|
||
|
||
// 处理图片
|
||
async processImages(tempFilePaths) {
|
||
const processedImages = [];
|
||
|
||
for (const tempPath of tempFilePaths) {
|
||
try {
|
||
// 获取图片信息
|
||
const imageInfo = await this.getImageInfo(tempPath);
|
||
|
||
// 检查文件大小
|
||
if (imageInfo.size > this.mediaConfig.image.maxSize) {
|
||
console.warn('⚠️ 图片过大,需要压缩:', imageInfo.size);
|
||
// 压缩图片
|
||
const compressedPath = await this.compressImage(tempPath);
|
||
imageInfo.tempFilePath = compressedPath;
|
||
}
|
||
|
||
// 生成缩略图
|
||
const thumbnailPath = await this.generateThumbnail(imageInfo.tempFilePath);
|
||
|
||
processedImages.push({
|
||
...imageInfo,
|
||
thumbnailPath: thumbnailPath,
|
||
type: 'image',
|
||
status: 'ready'
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('❌ 处理图片失败:', error);
|
||
}
|
||
}
|
||
|
||
return processedImages;
|
||
}
|
||
|
||
// 获取图片信息
|
||
async getImageInfo(src) {
|
||
return new Promise((resolve, reject) => {
|
||
wx.getImageInfo({
|
||
src: src,
|
||
success: (res) => {
|
||
// 获取文件大小
|
||
wx.getFileInfo({
|
||
filePath: src,
|
||
success: (fileInfo) => {
|
||
resolve({
|
||
...res,
|
||
size: fileInfo.size,
|
||
tempFilePath: src
|
||
});
|
||
},
|
||
fail: () => {
|
||
resolve({
|
||
...res,
|
||
size: 0,
|
||
tempFilePath: src
|
||
});
|
||
}
|
||
});
|
||
},
|
||
fail: reject
|
||
});
|
||
});
|
||
}
|
||
|
||
// 压缩图片
|
||
// 图片压缩处理函数
|
||
compressImage(tempFilePath) {
|
||
return new Promise((resolve, reject) => {
|
||
// 先获取图片信息,用于计算压缩比例
|
||
wx.getImageInfo({
|
||
src: tempFilePath,
|
||
success: (info) => {
|
||
// 根据图片尺寸计算合适的压缩比例
|
||
let quality = 0.6; // 初始质量
|
||
let width = info.width;
|
||
let height = info.height;
|
||
|
||
// 对于过大的图片先缩小尺寸
|
||
if (width > 1000 || height > 1000) {
|
||
const scale = 1000 / Math.max(width, height);
|
||
width = Math.floor(width * scale);
|
||
height = Math.floor(height * scale);
|
||
quality = 0.5; // 大图片适当降低质量
|
||
} else if (width > 500 || height > 500) {
|
||
quality = 0.55; // 中等图片适度压缩
|
||
}
|
||
|
||
// 执行压缩
|
||
wx.compressImage({
|
||
src: tempFilePath,
|
||
quality: quality, // 质量,0-100,这里用0.6表示60%质量
|
||
width: width, // 压缩后的宽度
|
||
height: height, // 压缩后的高度
|
||
success: (res) => {
|
||
resolve(res.tempFilePath);
|
||
},
|
||
fail: (err) => {
|
||
reject(err);
|
||
}
|
||
});
|
||
},
|
||
fail: (err) => {
|
||
reject(err);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
// 生成缩略图
|
||
async generateThumbnail(src) {
|
||
try {
|
||
// 创建canvas生成缩略图
|
||
const canvas = wx.createOffscreenCanvas({ type: '2d' });
|
||
const ctx = canvas.getContext('2d');
|
||
|
||
// 设置缩略图尺寸
|
||
const thumbnailSize = 200;
|
||
canvas.width = thumbnailSize;
|
||
canvas.height = thumbnailSize;
|
||
|
||
// 加载图片
|
||
const image = canvas.createImage();
|
||
|
||
return new Promise((resolve) => {
|
||
image.onload = () => {
|
||
// 计算绘制尺寸
|
||
const { drawWidth, drawHeight, drawX, drawY } = this.calculateDrawSize(
|
||
image.width,
|
||
image.height,
|
||
thumbnailSize,
|
||
thumbnailSize
|
||
);
|
||
|
||
// 绘制缩略图
|
||
ctx.drawImage(image, drawX, drawY, drawWidth, drawHeight);
|
||
|
||
// 导出为临时文件
|
||
wx.canvasToTempFilePath({
|
||
canvas: canvas,
|
||
success: (res) => {
|
||
resolve(res.tempFilePath);
|
||
},
|
||
fail: () => {
|
||
resolve(src); // 生成失败返回原图
|
||
}
|
||
});
|
||
};
|
||
|
||
image.onerror = () => {
|
||
resolve(src); // 加载失败返回原图
|
||
};
|
||
|
||
image.src = src;
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('❌ 生成缩略图失败:', error);
|
||
return src;
|
||
}
|
||
}
|
||
|
||
// 计算绘制尺寸
|
||
calculateDrawSize(imageWidth, imageHeight, canvasWidth, canvasHeight) {
|
||
const imageRatio = imageWidth / imageHeight;
|
||
const canvasRatio = canvasWidth / canvasHeight;
|
||
|
||
let drawWidth, drawHeight, drawX, drawY;
|
||
|
||
if (imageRatio > canvasRatio) {
|
||
// 图片更宽,以高度为准
|
||
drawHeight = canvasHeight;
|
||
drawWidth = drawHeight * imageRatio;
|
||
drawX = (canvasWidth - drawWidth) / 2;
|
||
drawY = 0;
|
||
} else {
|
||
// 图片更高,以宽度为准
|
||
drawWidth = canvasWidth;
|
||
drawHeight = drawWidth / imageRatio;
|
||
drawX = 0;
|
||
drawY = (canvasHeight - drawHeight) / 2;
|
||
}
|
||
|
||
return { drawWidth, drawHeight, drawX, drawY };
|
||
}
|
||
|
||
// 🎬 ===== 视频处理 =====
|
||
|
||
// 选择视频
|
||
async chooseVideo(options = {}) {
|
||
try {
|
||
const {
|
||
sourceType = ['album', 'camera'],
|
||
maxDuration = this.mediaConfig.video.maxDuration,
|
||
camera = 'back'
|
||
} = options;
|
||
|
||
const result = await new Promise((resolve, reject) => {
|
||
wx.chooseVideo({
|
||
sourceType: sourceType,
|
||
maxDuration: maxDuration,
|
||
camera: camera,
|
||
success: resolve,
|
||
fail: reject
|
||
});
|
||
});
|
||
|
||
// 验证和处理视频
|
||
const processedVideo = await this.processVideo(result);
|
||
|
||
return {
|
||
success: true,
|
||
video: processedVideo
|
||
};
|
||
|
||
} catch (error) {
|
||
console.error('❌ 选择视频失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error.errMsg || '选择视频失败'
|
||
};
|
||
}
|
||
}
|
||
|
||
// 处理视频
|
||
async processVideo(videoResult) {
|
||
try {
|
||
// 检查文件大小
|
||
if (videoResult.size > this.mediaConfig.video.maxSize) {
|
||
throw new Error('视频文件过大');
|
||
}
|
||
|
||
// 检查时长
|
||
if (videoResult.duration > this.mediaConfig.video.maxDuration) {
|
||
throw new Error('视频时长超出限制');
|
||
}
|
||
|
||
// 生成视频缩略图
|
||
const thumbnailPath = await this.generateVideoThumbnail(videoResult.tempFilePath);
|
||
|
||
return {
|
||
tempFilePath: videoResult.tempFilePath,
|
||
duration: videoResult.duration,
|
||
size: videoResult.size,
|
||
width: videoResult.width,
|
||
height: videoResult.height,
|
||
thumbnailPath: thumbnailPath,
|
||
type: 'video',
|
||
status: 'ready'
|
||
};
|
||
|
||
} catch (error) {
|
||
console.error('❌ 处理视频失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 生成视频缩略图
|
||
async generateVideoThumbnail(videoPath) {
|
||
try {
|
||
// 微信小程序暂不支持视频帧提取,使用默认图标
|
||
return null;
|
||
} catch (error) {
|
||
console.error('❌ 生成视频缩略图失败:', error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// 📄 ===== 文件处理 =====
|
||
|
||
// 选择文件
|
||
async chooseFile(options = {}) {
|
||
try {
|
||
const {
|
||
count = this.mediaConfig.file.maxCount,
|
||
type = 'all'
|
||
} = options;
|
||
|
||
const result = await new Promise((resolve, reject) => {
|
||
wx.chooseMessageFile({
|
||
count: count,
|
||
type: type,
|
||
success: resolve,
|
||
fail: reject
|
||
});
|
||
});
|
||
|
||
// 验证和处理文件
|
||
const processedFiles = await this.processFiles(result.tempFiles);
|
||
|
||
return {
|
||
success: true,
|
||
files: processedFiles
|
||
};
|
||
|
||
} catch (error) {
|
||
console.error('❌ 选择文件失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error.errMsg || '选择文件失败'
|
||
};
|
||
}
|
||
}
|
||
|
||
// 处理文件
|
||
async processFiles(tempFiles) {
|
||
const processedFiles = [];
|
||
|
||
for (const file of tempFiles) {
|
||
try {
|
||
// 检查文件大小
|
||
if (file.size > this.mediaConfig.file.maxSize) {
|
||
console.warn('⚠️ 文件过大:', file.name, file.size);
|
||
continue;
|
||
}
|
||
|
||
// 检查文件类型
|
||
const fileExtension = this.getFileExtension(file.name);
|
||
if (!this.isAllowedFileType(fileExtension)) {
|
||
console.warn('⚠️ 不支持的文件类型:', fileExtension);
|
||
continue;
|
||
}
|
||
|
||
processedFiles.push({
|
||
tempFilePath: file.path,
|
||
name: file.name,
|
||
size: file.size,
|
||
type: 'file',
|
||
extension: fileExtension,
|
||
status: 'ready'
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('❌ 处理文件失败:', error);
|
||
}
|
||
}
|
||
|
||
return processedFiles;
|
||
}
|
||
|
||
// 获取文件扩展名
|
||
getFileExtension(fileName) {
|
||
const lastDotIndex = fileName.lastIndexOf('.');
|
||
if (lastDotIndex === -1) return '';
|
||
return fileName.substring(lastDotIndex + 1).toLowerCase();
|
||
}
|
||
|
||
// 检查文件类型是否允许
|
||
isAllowedFileType(extension) {
|
||
return this.mediaConfig.file.allowedTypes.includes(extension);
|
||
}
|
||
|
||
// 📤 ===== 文件上传 =====
|
||
|
||
// 上传文件
|
||
async uploadFile(file, options = {}) {
|
||
try {
|
||
const {
|
||
onProgress,
|
||
onSuccess,
|
||
onError
|
||
} = options;
|
||
|
||
// 生成上传ID
|
||
const uploadId = this.generateUploadId();
|
||
|
||
// 添加到上传队列
|
||
const uploadTask = {
|
||
id: uploadId,
|
||
file: file,
|
||
status: 'uploading',
|
||
progress: 0,
|
||
onProgress: onProgress,
|
||
onSuccess: onSuccess,
|
||
onError: onError
|
||
};
|
||
|
||
this.currentUploads.set(uploadId, uploadTask);
|
||
|
||
// 执行上传
|
||
const result = await this.performUpload(uploadTask);
|
||
|
||
// 移除上传任务
|
||
this.currentUploads.delete(uploadId);
|
||
|
||
return result;
|
||
|
||
} catch (error) {
|
||
console.error('❌ 上传文件失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error.message
|
||
};
|
||
}
|
||
}
|
||
|
||
// 执行上传
|
||
async performUpload(uploadTask) {
|
||
return new Promise((resolve, reject) => {
|
||
const uploadTask_wx = wx.uploadFile({
|
||
url: `${apiClient.baseURL}/api/v1/files/upload`,
|
||
filePath: uploadTask.file.tempFilePath,
|
||
name: 'file',
|
||
header: {
|
||
'Authorization': `Bearer ${wx.getStorageSync('token')}`
|
||
},
|
||
formData: {
|
||
type: uploadTask.file.type,
|
||
name: uploadTask.file.name || 'unknown'
|
||
},
|
||
success: (res) => {
|
||
try {
|
||
const data = JSON.parse(res.data);
|
||
if (data.success) {
|
||
|
||
const result = {
|
||
success: true,
|
||
data: {
|
||
url: data.data.url,
|
||
fileId: data.data.fileId,
|
||
fileName: uploadTask.file.name,
|
||
fileSize: uploadTask.file.size,
|
||
fileType: uploadTask.file.type
|
||
}
|
||
};
|
||
|
||
if (uploadTask.onSuccess) {
|
||
uploadTask.onSuccess(result);
|
||
}
|
||
|
||
resolve(result);
|
||
} else {
|
||
throw new Error(data.error || '上传失败');
|
||
}
|
||
} catch (error) {
|
||
reject(error);
|
||
}
|
||
},
|
||
fail: (error) => {
|
||
console.error('❌ 上传请求失败:', error);
|
||
if (uploadTask.onError) {
|
||
uploadTask.onError(error);
|
||
}
|
||
reject(error);
|
||
}
|
||
});
|
||
|
||
// 监听上传进度
|
||
uploadTask_wx.onProgressUpdate((res) => {
|
||
uploadTask.progress = res.progress;
|
||
|
||
if (uploadTask.onProgress) {
|
||
uploadTask.onProgress({
|
||
progress: res.progress,
|
||
totalBytesSent: res.totalBytesSent,
|
||
totalBytesExpectedToSend: res.totalBytesExpectedToSend
|
||
});
|
||
}
|
||
});
|
||
|
||
// 保存上传任务引用
|
||
uploadTask.wxTask = uploadTask_wx;
|
||
});
|
||
}
|
||
|
||
// 取消上传
|
||
cancelUpload(uploadId) {
|
||
const uploadTask = this.currentUploads.get(uploadId);
|
||
if (uploadTask && uploadTask.wxTask) {
|
||
uploadTask.wxTask.abort();
|
||
this.currentUploads.delete(uploadId);
|
||
|
||
}
|
||
}
|
||
|
||
// 生成上传ID
|
||
generateUploadId() {
|
||
return `upload_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||
}
|
||
|
||
// 📥 ===== 文件下载和缓存 =====
|
||
|
||
// 下载文件
|
||
async downloadFile(url, options = {}) {
|
||
try {
|
||
const {
|
||
fileName,
|
||
onProgress,
|
||
useCache = true
|
||
} = options;
|
||
|
||
// 检查缓存
|
||
if (useCache) {
|
||
const cachedPath = this.getCachedFilePath(url);
|
||
if (cachedPath) {
|
||
|
||
return {
|
||
success: true,
|
||
tempFilePath: cachedPath,
|
||
cached: true
|
||
};
|
||
}
|
||
}
|
||
|
||
// 执行下载
|
||
const result = await this.performDownload(url, { fileName, onProgress });
|
||
|
||
// 缓存文件
|
||
if (result.success && useCache) {
|
||
this.cacheFile(url, result.tempFilePath);
|
||
}
|
||
|
||
return result;
|
||
|
||
} catch (error) {
|
||
console.error('❌ 下载文件失败:', error);
|
||
return {
|
||
success: false,
|
||
error: error.message
|
||
};
|
||
}
|
||
}
|
||
|
||
// 执行下载
|
||
async performDownload(url, options = {}) {
|
||
return new Promise((resolve, reject) => {
|
||
const downloadTask = wx.downloadFile({
|
||
url: url,
|
||
success: (res) => {
|
||
if (res.statusCode === 200) {
|
||
|
||
resolve({
|
||
success: true,
|
||
tempFilePath: res.tempFilePath,
|
||
cached: false
|
||
});
|
||
} else {
|
||
reject(new Error(`下载失败: ${res.statusCode}`));
|
||
}
|
||
},
|
||
fail: reject
|
||
});
|
||
|
||
// 监听下载进度
|
||
if (options.onProgress) {
|
||
downloadTask.onProgressUpdate((res) => {
|
||
options.onProgress({
|
||
progress: res.progress,
|
||
totalBytesWritten: res.totalBytesWritten,
|
||
totalBytesExpectedToWrite: res.totalBytesExpectedToWrite
|
||
});
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
// 检查存储权限
|
||
async checkStoragePermission() {
|
||
try {
|
||
const storageInfo = wx.getStorageInfoSync();
|
||
|
||
return true;
|
||
} catch (error) {
|
||
console.error('❌ 检查存储权限失败:', error);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// 加载缓存信息
|
||
async loadCacheInfo() {
|
||
try {
|
||
const cacheInfo = wx.getStorageSync('mediaCacheInfo') || {};
|
||
this.cacheStats = {
|
||
totalSize: cacheInfo.totalSize || 0,
|
||
fileCount: cacheInfo.fileCount || 0,
|
||
lastCleanup: cacheInfo.lastCleanup || 0
|
||
};
|
||
} catch (error) {
|
||
console.error('❌ 加载缓存信息失败:', error);
|
||
}
|
||
}
|
||
|
||
// 保存缓存信息
|
||
async saveCacheInfo() {
|
||
try {
|
||
wx.setStorageSync('mediaCacheInfo', this.cacheStats);
|
||
} catch (error) {
|
||
console.error('❌ 保存缓存信息失败:', error);
|
||
}
|
||
}
|
||
|
||
// 获取缓存文件路径
|
||
getCachedFilePath(url) {
|
||
const cacheKey = this.generateCacheKey(url);
|
||
return this.fileCache.get(cacheKey);
|
||
}
|
||
|
||
// 缓存文件
|
||
cacheFile(url, filePath) {
|
||
const cacheKey = this.generateCacheKey(url);
|
||
this.fileCache.set(cacheKey, filePath);
|
||
|
||
// 更新缓存统计
|
||
this.cacheStats.fileCount++;
|
||
this.saveCacheInfo();
|
||
}
|
||
|
||
// 生成缓存键
|
||
generateCacheKey(url) {
|
||
// 使用URL的hash作为缓存键
|
||
let hash = 0;
|
||
for (let i = 0; i < url.length; i++) {
|
||
const char = url.charCodeAt(i);
|
||
hash = ((hash << 5) - hash) + char;
|
||
hash = hash & hash; // 转换为32位整数
|
||
}
|
||
return `cache_${Math.abs(hash)}`;
|
||
}
|
||
|
||
// 启动缓存清理
|
||
startCacheCleanup() {
|
||
setInterval(() => {
|
||
this.performCacheCleanup();
|
||
}, this.mediaConfig.cache.cleanupInterval);
|
||
}
|
||
|
||
// 执行缓存清理
|
||
performCacheCleanup() {
|
||
try {
|
||
|
||
const now = Date.now();
|
||
const expireTime = this.mediaConfig.cache.expireTime;
|
||
|
||
// 清理过期缓存
|
||
for (const [key, filePath] of this.fileCache) {
|
||
try {
|
||
const stats = wx.getFileInfo({ filePath });
|
||
if (now - stats.createTime > expireTime) {
|
||
this.fileCache.delete(key);
|
||
// 删除文件
|
||
wx.removeSavedFile({ filePath });
|
||
}
|
||
} catch (error) {
|
||
// 文件不存在,从缓存中移除
|
||
this.fileCache.delete(key);
|
||
}
|
||
}
|
||
|
||
// 更新清理时间
|
||
this.cacheStats.lastCleanup = now;
|
||
this.saveCacheInfo();
|
||
|
||
} catch (error) {
|
||
console.error('❌ 缓存清理失败:', error);
|
||
}
|
||
}
|
||
|
||
// 获取媒体管理器状态
|
||
getStatus() {
|
||
return {
|
||
isInitialized: this.isInitialized,
|
||
uploadCount: this.currentUploads.size,
|
||
cacheStats: { ...this.cacheStats },
|
||
config: this.mediaConfig
|
||
};
|
||
}
|
||
|
||
// 清除所有缓存
|
||
clearAllCache() {
|
||
this.fileCache.clear();
|
||
this.cacheStats = {
|
||
totalSize: 0,
|
||
fileCount: 0,
|
||
lastCleanup: Date.now()
|
||
};
|
||
this.saveCacheInfo();
|
||
|
||
}
|
||
|
||
// 重置管理器
|
||
reset() {
|
||
// 取消所有上传任务
|
||
for (const [uploadId] of this.currentUploads) {
|
||
this.cancelUpload(uploadId);
|
||
}
|
||
|
||
this.clearAllCache();
|
||
}
|
||
}
|
||
|
||
// 创建全局实例
|
||
const mediaManager = new MediaManager();
|
||
|
||
module.exports = mediaManager;
|
||
|