import { requestUtils,clientApi } from "../index"; let urlRule = 'https://api.deotaland.aiIMGURL' export class FileServer { //文件上传缓存映射 - 静态属性,所有实例共享 static fileCacheMap = new Map(); constructor() { } //文件拼接 concatUrl(url) { return urlRule.replace('IMGURL',url) } /** * 从URL中提取有效的文件名 * @param {*} url 文件URL * @returns 有效的文件名 */ extractFileName(url) { // 如果是base64格式,根据MIME类型生成文件名 if (url.startsWith('data:')) { const mimeType = url.match(/data:([^;]+)/)?.[1]; const extension = this.getExtensionFromMimeType(mimeType); return `uploaded_file_${Date.now()}.${extension}`; } // 如果是普通URL,提取文件名并清理 try { const urlObj = new URL(url); const pathname = urlObj.pathname; const fileName = pathname.split('/').pop() || 'uploaded_file'; // 移除查询参数和哈希 const cleanFileName = fileName.split('?')[0].split('#')[0]; // 如果没有扩展名,尝试从URL路径推断 if (!cleanFileName.includes('.')) { const extension = this.inferExtensionFromPath(pathname); return cleanFileName + (extension ? `.${extension}` : ''); } return cleanFileName || `uploaded_file_${Date.now()}`; } catch (error) { // URL解析失败,使用默认文件名 return `uploaded_file_${Date.now()}`; } } /** * 从MIME类型获取文件扩展名 */ getExtensionFromMimeType(mimeType) { const mimeMap = { 'image/jpeg': 'jpg', 'image/jpg': 'jpg', 'image/png': 'png', 'image/gif': 'gif', 'image/bmp': 'bmp', 'image/webp': 'webp', 'image/svg+xml': 'svg', 'application/pdf': 'pdf', 'text/plain': 'txt', 'application/json': 'json' }; return mimeMap[mimeType] || 'bin'; } /** * 从URL路径推断文件扩展名 */ inferExtensionFromPath(pathname) { // 常见的图片路径模式 if (pathname.includes('/image/') || pathname.includes('/img/')) return 'jpg'; if (pathname.includes('/photo/')) return 'jpg'; if (pathname.includes('/picture/')) return 'png'; return null; } //轮询获取并发时的线上文件映射 async pollFileCacheMap(cacheKey) { return new Promise((resolve, reject) => { const interval = setInterval(() => { if (FileServer.fileCacheMap.get(cacheKey)!='loading') { clearInterval(interval); resolve(FileServer.fileCacheMap.get(cacheKey)); } }, 1000); // 每1秒检查一次 }); } //上传文件 async uploadFile(url) { const cacheKey = url.slice(-8); return new Promise(async (resolve, reject) => { if(FileServer.fileCacheMap.has(cacheKey)&&FileServer.fileCacheMap.get(cacheKey)!='loading'){ resolve(this.concatUrl(FileServer.fileCacheMap.get(cacheKey))); return; } if(FileServer.fileCacheMap.get(cacheKey)=='loading'){ const loadUrl = await this.pollFileCacheMap(cacheKey); resolve(this.concatUrl(loadUrl)); return; } FileServer.fileCacheMap.set(cacheKey,'loading'); const file = await this.fileToBlob(url);//将文件或者base64文件转为blob对象 const formData = new FormData(); // 从URL中提取文件名,如果没有则使用默认文件名 const fileName = this.extractFileName(url); formData.append('file', file, fileName); try { const response = await requestUtils.upload(clientApi.default.UPLOAD.url, formData); if(response.code==0){ let data = response.data; if(data.url){ // 截取后八位作为缓存 key FileServer.fileCacheMap.set(cacheKey, data.url); resolve(urlRule.replace('IMGURL',data.url)); } } } catch (error) { reject(error); console.error('上传文件失败:', error); throw error; } }) } //文件文件或者base64文件转为blob对象 fileToBlob(fileOrBase64) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', fileOrBase64); xhr.responseType = 'blob'; xhr.onload = () => { if (xhr.status === 200) { resolve(xhr.response); } else { reject(new Error('文件转换失败')); } }; xhr.onerror = reject; xhr.send(); }); } //本地文件或者oss文件转为base64格式 async fileToBase64(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.responseType = 'blob'; xhr.onload = () => { if (xhr.status === 200) { const reader = new FileReader(); reader.readAsDataURL(xhr.response); reader.onloadend = () => { resolve(reader.result); }; } else { reject(new Error('文件转换失败')); } }; xhr.onerror = reject; xhr.send(); }); } }