🚀 前端图片上传 OSS 实践分享(el-upload + PUT 上传到阿里云)
在实际业务中,我们常常需要将图片上传至阿里云 OSS。以下是我在项目中实践的一种实现方式,结合 Element UI 的 el-upload 组件 + 后端返回的 OSS 预签名地址实现直传,分享给大家。
📸 1. 使用 el-upload 组件上传图片
<el-form-item
prop="pictureUrls"
label="图片"
>
<el-upload
ref="uploadRef"
class="upload-demo"
drag
multiple
:file-list="fileList"
action=""
:before-upload="beforeAvatarUpload"
:on-remove="handleRemove"
:limit="10"
>
<i class="el-icon-upload" />
<div class="el-upload__tip" slot="tip">
{{ showTip }}
</div>
</el-upload>
</el-form-item>
file-list: 上传的文件列表,例如:
[{ name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg' }]before-upload: 上传前校验钩子,控制文件是否允许上传on-remove: 删除文件时触发,可同步处理后端图片数据
🔌 2. 后端接口返回结构(预签名上传地址)
{
"code": 0,
"data": {
"uploadUrl": "https://oss-url.com/xxx?Signature=xxx", // PUT 上传地址
"originUrl": "https://oss-url.com/xxx" // 图片访问地址
}
}
🔁 3. 上传流程概览
1️⃣ 向后端申请预签名的上传地址 uploadUrl
2️⃣ 使用 axios.put(uploadUrl, file) 上传图片至 OSS
3️⃣ 上传成功后,记录 originUrl 作为图片访问地址
🔍 4. 上传前校验尺寸 + 手动上传逻辑(核心)
beforeAvatarUpload(file) {
const type = this.form.pictureType;
if (typeof type !== 'number' && !type) {
this.$message.error('请先选择图片分类');
return false;
}
const { type: fileType } = file;
if (!fileType.includes('image')) {
this.$message.error('只支持上传图片');
return false;
}
const reader = new FileReader();
reader.onload = (event) => {
const img = new Image();
img.onload = async () => {
const { width, height } = img;
// 校验图片尺寸
const isValid = (type === pictureTypeMap.card && width === 240 && height === 144)
|| (type === pictureTypeMap.business && width === 220 && height === 164)
|| (type === pictureTypeMap.detail && width === 702 && height === 364);
if (!isValid) {
this.$message.error('图片尺寸不正确,请根据图片类型选择对应尺寸');
return;
}
// ✅ 校验通过后手动上传
this.uploadImage(file);
// ✅ 添加到 fileList 以便展示
this.fileList.push(file);
};
img.onerror = () => {
this.$message.error('图片加载失败');
};
img.src = event.target.result;
};
reader.readAsDataURL(file);
// ❗️阻止 el-upload 的默认上传行为
return false;
}
📤 5. 上传图片至 OSS 的核心逻辑
async uploadImage(file) {
this.loading = true;
try {
// 1. 调用后端,获取 uploadUrl + originUrl
const { code, data } = await uploadCouponMallPicture({
imageType: 83,
type: 3,
isPrivate: false,
contentType: file.type,
});
// 2. 使用 PUT 请求上传图片到 OSS
if (code === 0 && data?.uploadUrl && data?.originUrl) {
const response = await axios.put(data.uploadUrl, file, {
headers: {
'Content-Type': file.type,
},
});
if (response.status === 200) {
this.$message.success('成功上传到 OSS!');
}
// 3. 成功后更新 fileList 和 imgList
this.fileList = this.$refs.uploadRef.uploadFiles;
this.imgList.push({
...data,
uid: file.uid,
url: data.originUrl,
});
}
} catch (e) {
this.$message.error('上传失败,请重试');
this.fileList.pop(file);
} finally {
this.loading = false;
}
}
🗑️ 6. 删除文件处理
// 删除图片
handleRemove(file, fileList) {
// file 是当前被删除的文件,fileList 是当前剩余的文件列表
this.fileList = fileList;
// 已上传成功的cdn链接列表,也需要同步处理:
this.imgList = this.imgList.filter(img => img.uid !== file.uid);
},
⚠️ 注意事项
| 项目 | 说明 |
|---|---|
| 上传方式 | 必须使用 PUT 请求(不是 POST) |
| Content-Type | 必须设置为 file.type,如 image/jpeg |
| 请求体 | 不要用 form-data,文件内容就是 body |
| URL 有有效期 | 过期会 403,需重新获取 |
| OSS 跨域 | Bucket 必须允许跨域 PUT 上传(需后端配置) |