1. 整体流程概述
前端使用阿里云OSS上传文件的完整流程如下:
- 调用后端接口获取临时密钥
- 使用临时密钥初始化OSS客户端
- 调用OSS客户端上传文件
- 获取上传成功后的文件URL
2. 后端接口调用(获取临时密钥)
2.1 接口信息
- 接口地址 : /pc/file/sts
- 请求方式 :GET
- 返回格式 :JSON
2.2 返回参数说明
javascript
{
"code": 200, // 状态码,0或200表示成功
"msg": "操作成功",
"data": {
"accessKeyId": "STS.NZQpZ...", // 临时AccessKeyId
"accessKeySecret": "LJ8Z8...", // 临时AccessKeySecret
"securityToken": "CAIS+...", // 临时安全令牌
"endpoint": "oss-cn-hangzhou.aliyuncs.com", // OSS地域节点
"bucketName": "rh-app-private", // OSS存储空间名称
"bucketDomain": "https://rh-app-private.oss-cn-hangzhou.aliyuncs.com" // OSS存储空间域名
}
}
2.3 前端调用代码
javascript
import api from '@/utils/api'
export const getSTSToken = async () => {
try {
const response = await api.get('/pc/file/sts')
if (response.code === 200) {
return response.data
} else {
throw new Error('获取STS密钥失败: ' + (response.msg || '未知错误'))
}
} catch (error) {
console.error('获取STS密钥失败:', error)
throw error
}
}
3. OSS客户端初始化
3.1 安装阿里云OSS SDK
javascript
npm install ali-oss
3.2 初始化OSS客户端
javascript
import OSS from 'ali-oss'
import { getSTSToken } from './sts'
let client = null
export const initOSSClient = async () => {
try {
const token = await getSTSToken()
// 解析region:从endpoint中提取region部分
let region = token.endpoint
if (token.endpoint && token.endpoint.includes('.')) {
const parts = token.endpoint.split('.')
if (parts.length >= 3 && parts[0].startsWith('oss-')) {
region = parts[0]
} else {
region = parts[0]
console.warn('非标准OSS endpoint格式:', region)
}
}
// 存储bucketDomain以便上传时使用
window.ossBucketDomain = token.bucketDomain
// 创建OSS客户端实例
client = new OSS({
region: region, // 区域
endpoint: token.endpoint, // 地域节点
accessKeyId: token.accessKeyId, // 临时AccessKeyId
accessKeySecret: token.accessKeySecret, // 临时AccessKeySecret
stsToken: token.securityToken, // 临时安全令牌
bucket: token.bucketName, // 存储空间名称
// 配置自动刷新临时密钥
refreshSTSToken: async () => {
const newToken = await getSTSToken()
return {
accessKeyId: newToken.accessKeyId,
accessKeySecret: newToken.accessKeySecret,
stsToken: newToken.securityToken
}
},
// 刷新间隔:5分钟
refreshSTSTokenInterval: 300000
})
console.log('OSS客户端初始化成功')
return client
} catch (error) {
console.error('OSS客户端初始化失败:', error)
throw error
}
}
4. 文件上传实现
4.1 文件上传函数
javascript
export const uploadFileToOSS = async (file, type, phone, name) => {
try {
// 确保OSS客户端已初始化
if (!client) {
await initOSSClient()
}
// 获取文件后缀
const fileExt = file.name.substring(file.name.lastIndexOf('.'))
const timestamp = Date.now()
// 根据文件类型生成不同的路径和文件名
let filePath
if (type === 'front' || type === 'back') {
// 身份证照片 - /Doctor_ID/手机号_医生姓名_时间戳.图片后缀
const folder = '/Doctor_ID/'
filePath = `${folder}${phone}_${name}_${timestamp}${fileExt}`
} else if (type === 'medicalFront' || type === 'medicalBack') {
// 医师执业资格证照片 - /Doctor_PPC/手机号_医生姓名_时间戳.图片后缀
const folder = '/Doctor_PPC/'
filePath = `${folder}${phone}_${name}_${timestamp}${fileExt}`
} else {
// 其他类型文件
filePath = `${type}-${timestamp}-${Math.random().toString(36).substring(2, 10)}${fileExt}`
}
// 上传文件
const result = await client.put(filePath, file)
// 返回文件URL - 优先使用bucketDomain构建URL
let fileUrl = result.url
if (window.ossBucketDomain && filePath) {
// 移除filePath开头可能的斜杠
const cleanFilePath = filePath.startsWith('/') ? filePath.substring(1) : filePath
fileUrl = `${window.ossBucketDomain}/${cleanFilePath}`
}
return fileUrl
} catch (error) {
console.error('OSS上传失败:', error)
throw new Error('文件上传失败')
}
}
4.2 调用上传函数
javascript
// 在Vue组件中调用
const handleFileChange = async (event, type) => {
const file = event.target.files[0]
if (!file) return
try {
// 显示上传中提示
showToast({ message: '上传中...', duration: 0 })
// 上传到OSS,传递必要参数
const imageUrl = await uploadFileToOSS(file, type, form.phone, form.name)
// 存储图片URL
if (type === 'front') {
form.frontIdCard = imageUrl
} else if (type === 'back') {
form.backIdCard = imageUrl
} else if (type === 'medicalFront') {
form.medicalFront = imageUrl
} else if (type === 'medicalBack') {
form.medicalBack = imageUrl
}
showToast('图片上传成功')
} catch (error) {
console.error('图片上传失败:', error)
showToast('图片上传失败,请稍后重试')
// 清空文件输入
event.target.value = ''
}
}
5. 文件命名规范
5.1 身份证照片
- 路径格式 : /Doctor_ID/手机号_医生姓名_时间戳.图片后缀
- 示例 : /Doctor_ID/13800138000_张三_1734478800000.jpg
5.2 医师执业资格证照片
- 路径格式 : /Doctor_PPC/手机号_医生姓名_时间戳.图片后缀
- 示例 : /Doctor_PPC/13800138000_张三_1734478800000.jpg
6. 错误处理
6.1 接口调用错误
javascript
try {
const response = await api.get('/pc/file/sts')
// 处理响应
} catch (error) {
console.error('获取STS密钥失败:', error)
showToast('OSS初始化失败,图片上传功能可能不可用')
}
6.2 文件上传错误
javascript
try {
const result = await client.put(filePath, file)
// 处理上传结果
} catch (error) {
console.error('OSS上传失败:', error)
showToast('文件上传失败,请稍后重试')
}
7. 关键代码示例
7.1 完整的OSS工具类(src/utils/oss.js)
javascript
import OSS from 'ali-oss'
import api from '@/utils/api'
// 全局OSS客户端实例
let client = null
// 获取STS临时密钥
export const getSTSToken = async () => {
try {
const response = await api.get('/pc/file/sts')
if (response.code === 0 || response.code === 200) {
return response.data
} else {
throw new Error('获取STS密钥失败: ' + (response.msg || '未知错误'))
}
} catch (error) {
console.error('获取STS密钥失败:', error)
throw error
}
}
// 初始化OSS客户端
export const initOSSClient = async () => {
try {
const token = await getSTSToken()
// 解析region
let region = token.endpoint
if (token.endpoint && token.endpoint.includes('.')) {
const parts = token.endpoint.split('.')
if (parts.length >= 3 && parts[0].startsWith('oss-')) {
region = parts[0]
} else {
region = parts[0]
console.warn('非标准OSS endpoint格式:', region)
}
}
// 存储bucketDomain
window.ossBucketDomain = token.bucketDomain
// 创建OSS客户端实例
client = new OSS({
region: region,
endpoint: token.endpoint,
accessKeyId: token.accessKeyId,
accessKeySecret: token.accessKeySecret,
stsToken: token.securityToken,
bucket: token.bucketName,
refreshSTSToken: async () => {
const newToken = await getSTSToken()
return {
accessKeyId: newToken.accessKeyId,
accessKeySecret: newToken.accessKeySecret,
stsToken: newToken.securityToken
}
},
refreshSTSTokenInterval: 300000
})
return client
} catch (error) {
console.error('OSS客户端初始化失败:', error)
throw error
}
}
// 上传文件到OSS
export const uploadFileToOSS = async (file, type, phone, name) => {
try {
if (!client) {
await initOSSClient()
}
const fileExt = file.name.substring(file.name.lastIndexOf('.'))
const timestamp = Date.now()
let filePath
if (type === 'front' || type === 'back') {
const folder = '/Doctor_ID/'
filePath = `${folder}${phone}_${name}_${timestamp}${fileExt}`
} else if (type === 'medicalFront' || type === 'medicalBack') {
const folder = '/Doctor_PPC/'
filePath = `${folder}${phone}_${name}_${timestamp}${fileExt}`
} else {
filePath = `${type}-${timestamp}-${Math.random().toString(36).substring(2, 10)}${fileExt}`
}
const result = await client.put(filePath, file)
let fileUrl = result.url
if (window.ossBucketDomain && filePath) {
const cleanFilePath = filePath.startsWith('/') ? filePath.substring(1) : filePath
fileUrl = `${window.ossBucketDomain}/${cleanFilePath}`
}
return fileUrl
} catch (error) {
console.error('OSS上传失败:', error)
throw new Error('文件上传失败')
}
}
8. 注意事项
- 临时密钥有效期 :临时密钥通常有一定的有效期(默认1小时),代码中配置了自动刷新机制
- 文件大小限制 :建议在前端添加文件大小限制(如10MB),避免上传过大文件
- 错误处理 :完善的错误处理可以提高用户体验
- CORS配置 :确保OSS存储空间已正确配置CORS规则,允许前端域名访问
- 网络稳定性 :考虑添加重试机制,提高弱网络环境下的上传成功率
通过以上流程,前端可以安全、高效地将文件上传到阿里云OSS,并获取可用的文件URL用于后续业务处理。