# COS 预签名 URL 直传指南(推荐方式) ## 🎯 为什么使用预签名 URL? 相比 POST 表单上传,预签名 URL 直传更简单: - ✅ **无需复杂的表单字段** - 只需要一个 URL - ✅ **前端代码更简洁** - 直接 PUT 文件即可 - ✅ **自动处理 CORS** - 预签名 URL 包含签名,不受 CORS 限制 - ✅ **更安全** - URL 有过期时间,临时授权 --- ## 📋 完整的前端上传代码 ### Vue 3 + Element Plus 示例 ```vue ``` --- ### 原生 JavaScript 示例 ```javascript async function uploadFile(file, userId) { try { // 1. 获取预签名 URL const signResponse = await fetch('/user/oss/presigned-url', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem('token') }, body: JSON.stringify({ fileName: file.name, userId: userId }) }); const signResult = await signResponse.json(); if (signResult.code !== 200) { throw new Error(signResult.message); } const { uploadUrl, fileUrl } = signResult.data; // 2. 直接 PUT 文件到预签名 URL const uploadResponse = await fetch(uploadUrl, { method: 'PUT', body: file, headers: { 'Content-Type': file.type } }); if (!uploadResponse.ok) { throw new Error('上传失败'); } // 3. 上传成功,返回文件访问地址 console.log('上传成功,文件地址:', fileUrl); return fileUrl; } catch (error) { console.error('上传失败:', error); throw error; } } // 使用示例 document.getElementById('fileInput').addEventListener('change', async (e) => { const file = e.target.files[0]; if (file) { try { const fileUrl = await uploadFile(file, '123'); alert('上传成功: ' + fileUrl); } catch (error) { alert('上传失败: ' + error.message); } } }); ``` --- ### React 示例 ```jsx import { useState } from 'react'; import { message } from 'antd'; function FileUpload() { const [uploading, setUploading] = useState(false); const handleUpload = async (file) => { setUploading(true); try { // 1. 获取预签名 URL const signResponse = await fetch('/user/oss/presigned-url', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}` }, body: JSON.stringify({ fileName: file.name, userId: '123' }) }); const signResult = await signResponse.json(); if (signResult.code !== 200) { throw new Error(signResult.message); } const { uploadUrl, fileUrl } = signResult.data; // 2. PUT 文件到预签名 URL const uploadResponse = await fetch(uploadUrl, { method: 'PUT', body: file, headers: { 'Content-Type': file.type } }); if (!uploadResponse.ok) { throw new Error('上传失败'); } message.success('上传成功'); console.log('文件地址:', fileUrl); return fileUrl; } catch (error) { console.error('上传失败:', error); message.error('上传失败: ' + error.message); } finally { setUploading(false); } }; return ( handleUpload(e.target.files[0])} disabled={uploading} /> ); } ``` --- ## 📡 API 接口说明 ### 接口地址 ``` POST /user/oss/presigned-url GET /user/oss/presigned-url ``` ### 请求参数(POST 方式) ```json { "fileName": "avatar.jpg", "userId": "123" } ``` ### 响应数据 ```json { "code": 200, "message": "预签名URL生成成功", "data": { "uploadUrl": "https://oss-1818ai-user-img-1302947942.cos.ap-guangzhou.myqcloud.com/user_img/xxx.jpg?sign=...", "fileUrl": "https://oss-1818ai-user-img-1302947942.cos.ap-guangzhou.myqcloud.com/user_img/xxx.jpg", "fileName": "avatar.jpg", "expiresIn": 3600 } } ``` ### 字段说明 | 字段 | 说明 | |------|------| | `uploadUrl` | 预签名上传 URL(包含签名,有效期1小时) | | `fileUrl` | 文件访问地址(永久有效) | | `fileName` | 文件名 | | `expiresIn` | 签名有效期(秒) | --- ## 🔧 上传流程 ``` ┌─────────┐ 1. 请求预签名URL ┌─────────┐ │ │ ────────────────────────> │ │ │ 前端 │ │ 后端 │ │ │ <──────────────────────── │ │ └─────────┘ 2. 返回预签名URL └─────────┘ │ │ 3. PUT 文件到预签名URL ↓ ┌─────────┐ │ COS │ │ 存储桶 │ └─────────┘ ``` --- ## ✅ 优势对比 | 特性 | 预签名 URL 直传 | POST 表单上传 | |------|----------------|--------------| | **前端代码** | ✅ 简单(一个 PUT 请求) | ❌ 复杂(多个表单字段) | | **CORS 配置** | ✅ 不需要(签名在 URL 中) | ❌ 需要配置 | | **字段匹配** | ✅ 无需关心字段名 | ❌ 必须匹配 COS 字段名 | | **错误排查** | ✅ 简单 | ❌ 复杂(403 签名错误) | | **安全性** | ✅ URL 有过期时间 | ✅ Policy 有过期时间 | --- ## ⚠️ 注意事项 1. **预签名 URL 有效期** - 默认 1 小时(3600 秒) - 过期后需要重新获取 2. **Content-Type** - PUT 请求时建议设置正确的 Content-Type - 例如:`image/jpeg`, `image/png` 3. **文件大小限制** - 默认最大 100MB - 可在后端配置中调整 4. **文件命名** - 后端会自动生成唯一文件名(UUID) - 避免文件名冲突 --- ## 🧪 测试命令 ```bash # 1. 获取预签名 URL curl -X POST http://localhost:8083/user/oss/presigned-url \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{"fileName":"test.jpg","userId":"123"}' # 2. 使用预签名 URL 上传文件 curl -X PUT "<返回的uploadUrl>" \ -H "Content-Type: image/jpeg" \ --data-binary "@/path/to/test.jpg" # 3. 访问文件 curl "<返回的fileUrl>" ``` --- ## 🎉 总结 使用预签名 URL 直传是最简单、最可靠的方式: - ✅ 前端只需两步:获取 URL → PUT 文件 - ✅ 无需配置 CORS - ✅ 无需关心复杂的表单字段 - ✅ 错误排查简单 **强烈推荐使用这种方式!**