# 管理端OSS上传使用示例 ## 🚀 快速开始 ### 1. 获取管理员Token ```javascript // 管理员登录获取Token const login = async (username, password) => { const response = await fetch('/admin/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); const result = await response.json(); return result.data.token; // 保存这个token }; ``` ### 2. 基础文件上传 ```javascript // 简单的文件上传函数 async function uploadFile(file, directory = 'uploads') { const token = localStorage.getItem('adminToken'); try { // 1. 获取上传签名 const signResponse = await fetch('/admin/oss/post-signature', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ fileName: file.name, directory: directory }) }); const { data: signature } = await signResponse.json(); // 2. 构建上传表单 - 使用正确的字段名 const formData = new FormData(); const fileKey = `${signature.dir}${Date.now()}_${file.name}`; formData.append('key', fileKey); formData.append('policy', signature.policy); formData.append('x-oss-credential', signature.x_oss_credential); formData.append('x-oss-date', signature.x_oss_date); formData.append('x-oss-signature-version', signature.x_oss_signature_version); formData.append('x-oss-signature', signature.x_oss_signature); formData.append('success_action_status', '200'); formData.append('file', file); // 3. 上传到OSS const uploadResponse = await fetch(signature.host, { method: 'POST', body: formData }); if (uploadResponse.ok) { const fileUrl = `${signature.host}/${fileKey}`; console.log('上传成功:', fileUrl); return { success: true, url: fileUrl, key: fileKey }; } throw new Error('上传失败'); } catch (error) { console.error('上传出错:', error); return { success: false, error: error.message }; } } // 使用示例 document.getElementById('fileInput').addEventListener('change', async (e) => { const file = e.target.files[0]; if (file) { const result = await uploadFile(file, 'banners'); if (result.success) { alert('上传成功: ' + result.url); } } }); ``` ## 📋 常用场景示例 ### Banner图片上传 ```html ``` ```javascript // JavaScript async function uploadBanner() { const fileInput = document.getElementById('bannerFile'); const file = fileInput.files[0]; if (!file) { alert('请选择文件'); return; } // 显示进度 document.getElementById('uploadProgress').style.display = 'block'; try { const result = await uploadFile(file, 'banners'); if (result.success) { alert('Banner上传成功!\n文件地址: ' + result.url); // 这里可以保存到数据库 saveBannerToDatabase(result.url, result.key); } } finally { document.getElementById('uploadProgress').style.display = 'none'; } } // 保存Banner到数据库的示例 async function saveBannerToDatabase(imageUrl, objectKey) { const token = localStorage.getItem('adminToken'); await fetch('/admin/banners/create', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ image: imageUrl, title: '新Banner', description: '通过OSS上传的Banner图片', linkType: 'internal', link: '/', sortOrder: 1, isEnabled: true }) }); } ``` ### 批量文件管理 ```javascript // 批量删除文件 async function deleteMultipleFiles(fileKeys) { const token = localStorage.getItem('adminToken'); const response = await fetch('/admin/oss/batch-delete', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(fileKeys) }); const result = await response.json(); console.log('删除结果:', result.data); alert(`删除完成: 成功${result.data.successCount}个,失败${result.data.failedCount}个`); } // 使用示例 const filesToDelete = [ 'user_img/banners/old_banner1.jpg', 'user_img/banners/old_banner2.jpg' ]; deleteMultipleFiles(filesToDelete); ``` ### Vue.js 组件示例 ```vue ``` ## 🛠️ 工具函数 ```javascript // OSS上传工具类 class AdminOssManager { constructor(baseURL, getToken) { this.baseURL = baseURL; this.getToken = getToken; // 获取token的函数 } // 上传文件 async upload(file, directory = 'uploads', options = {}) { const { maxSizeMB = Math.ceil(file.size / (1024 * 1024)), onProgress = () => {}, onSuccess = () => {}, onError = () => {} } = options; try { onProgress(0); // 获取签名 const signature = await this.getSignature(file.name, directory, maxSizeMB); onProgress(20); // 上传文件 const result = await this.uploadToOss(file, signature, (progress) => { onProgress(20 + progress * 0.8); // 20-100 }); onSuccess(result); return result; } catch (error) { onError(error); throw error; } } // 获取上传签名 async getSignature(fileName, directory, maxSizeMB) { const response = await fetch(`${this.baseURL}/admin/oss/post-signature`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.getToken()}` }, body: JSON.stringify({ fileName, directory, maxSizeMB }) }); const result = await response.json(); if (result.code !== 200) { throw new Error(result.message); } return result.data; } // 上传到OSS async uploadToOss(file, signature, onProgress) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); const formData = new FormData(); const fileKey = `${signature.dir}${this.generateFileName(file.name)}`; // 构建表单数据 formData.append('key', fileKey); formData.append('policy', signature.policy); // 注意:不需要这个字段,OSS POST V4使用x-oss-credential formData.append('x-oss-signature-version', signature.x_oss_signature_version); formData.append('x-oss-credential', signature.x_oss_credential); formData.append('x-oss-date', signature.x_oss_date); formData.append('x-oss-signature', signature.x_oss_signature); formData.append('success_action_status', '200'); formData.append('file', file); // 监听进度 xhr.upload.addEventListener('progress', (e) => { if (e.lengthComputable) { const progress = (e.loaded / e.total) * 100; onProgress(progress); } }); xhr.addEventListener('load', () => { if (xhr.status === 200) { resolve({ success: true, url: `${signature.host}/${fileKey}`, key: fileKey }); } else { reject(new Error(`Upload failed: ${xhr.status}`)); } }); xhr.addEventListener('error', () => { reject(new Error('Upload failed')); }); xhr.open('POST', signature.host); xhr.send(formData); }); } // 删除文件 async delete(objectKey) { const response = await fetch(`${this.baseURL}/admin/oss/file?objectKey=${encodeURIComponent(objectKey)}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${this.getToken()}` } }); const result = await response.json(); return result.code === 200; } // 批量删除 async batchDelete(objectKeys) { const response = await fetch(`${this.baseURL}/admin/oss/batch-delete`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.getToken()}` }, body: JSON.stringify(objectKeys) }); const result = await response.json(); return result.data; } // 生成唯一文件名 generateFileName(originalName) { const timestamp = Date.now(); const random = Math.random().toString(36).substring(2); const ext = originalName.substring(originalName.lastIndexOf('.')); return `${timestamp}_${random}${ext}`; } } // 使用示例 const ossManager = new AdminOssManager( 'https://your-api.com', () => localStorage.getItem('adminToken') ); // 上传文件 const uploadFile = async (file) => { try { const result = await ossManager.upload(file, 'banners', { onProgress: (progress) => console.log(`上传进度: ${progress}%`), onSuccess: (result) => console.log('上传成功:', result.url), onError: (error) => console.error('上传失败:', error) }); return result; } catch (error) { console.error('上传出错:', error); } }; ``` ## 📱 移动端适配 ```javascript // 移动端文件选择和上传 class MobileOssUpload { constructor(ossManager) { this.ossManager = ossManager; } // 选择并上传图片(支持相机和相册) async selectAndUploadImage(directory = 'images') { return new Promise((resolve, reject) => { const input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*'; input.capture = 'environment'; // 优先使用摄像头 input.onchange = async (e) => { const file = e.target.files[0]; if (file) { try { // 压缩图片(可选) const compressedFile = await this.compressImage(file); const result = await this.ossManager.upload(compressedFile, directory); resolve(result); } catch (error) { reject(error); } } }; input.click(); }); } // 图片压缩 async compressImage(file, quality = 0.8, maxWidth = 1920) { return new Promise((resolve) => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); const img = new Image(); img.onload = () => { // 计算压缩后的尺寸 let { width, height } = img; if (width > maxWidth) { height = (height * maxWidth) / width; width = maxWidth; } canvas.width = width; canvas.height = height; // 绘制压缩后的图片 ctx.drawImage(img, 0, 0, width, height); canvas.toBlob((blob) => { const compressedFile = new File([blob], file.name, { type: file.type, lastModified: Date.now() }); resolve(compressedFile); }, file.type, quality); }; img.src = URL.createObjectURL(file); }); } } // 使用示例 const mobileUpload = new MobileOssUpload(ossManager); // 移动端上传按钮 document.getElementById('mobileUploadBtn').addEventListener('click', async () => { try { const result = await mobileUpload.selectAndUploadImage('mobile'); alert('上传成功: ' + result.url); } catch (error) { alert('上传失败: ' + error.message); } }); ``` ## 🔧 调试工具 ```javascript // 调试和测试工具 const OssDebugger = { // 测试上传配置 async testConfig() { try { const response = await fetch('/admin/oss/upload-config', { headers: { 'Authorization': `Bearer ${localStorage.getItem('adminToken')}` } }); const result = await response.json(); console.log('上传配置:', result.data); return result.data; } catch (error) { console.error('获取配置失败:', error); } }, // 测试文件上传 async testUpload() { // 创建一个测试文件 const testContent = 'This is a test file for OSS upload'; const blob = new Blob([testContent], { type: 'text/plain' }); const testFile = new File([blob], 'test.txt', { type: 'text/plain' }); try { const result = await uploadFile(testFile, 'test'); console.log('测试上传结果:', result); // 测试删除 if (result.success) { await this.testDelete(result.key); } } catch (error) { console.error('测试上传失败:', error); } }, // 测试文件删除 async testDelete(objectKey) { try { const token = localStorage.getItem('adminToken'); const response = await fetch(`/admin/oss/file?objectKey=${encodeURIComponent(objectKey)}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }); const result = await response.json(); console.log('测试删除结果:', result); } catch (error) { console.error('测试删除失败:', error); } } }; // 在控制台运行测试 // OssDebugger.testConfig(); // OssDebugger.testUpload(); ``` --- ## 📝 注意事项 1. **Token管理**: 确保Token有效且不过期 2. **文件命名**: 建议使用时间戳+随机数避免重名 3. **错误处理**: 做好网络异常和服务器错误的处理 4. **进度显示**: 大文件上传时显示进度提升用户体验 5. **移动端优化**: 考虑移动设备的网络和性能限制 --- *更多详细信息请参考: [管理端OSS上传API文档](./admin-oss-upload-api.md)*