需求背景:h5上传图片到oss,并返回图片的链接
参考资料:
阿里云oss
multipartUpload api
图片纯前端JS压缩的实现
注意:以下不包括业务代码,纯个人处理的oss上传封装,替换成自己的临时凭证的接口即可使用
const OSS = require('ali-oss');
/**
*
* @param {图片base64位地址} url
* @returns
*/
const loadImage = function (url) {
return new Promise((resolve, reject) => {
let img = new Image();
img.src = url;
img.onload = () => resolve(img);
img.onerror = reject;
})
}
/**
* 压缩图片、减少带宽
* @param {*} file
* @returns
*/
const compressImg = async function (file) {
// 建立Canvas对象(画布)
let canvas = document.createElement('canvas'),
// 获取对应的CanvasRenderingContext2D对象(画笔)
context = canvas.getContext('2d');
let img = await loadImage(file.content);
// 图片原始尺寸
let originWidth = img.width,
originHeight = img.height,
// 最大尺寸限制
maxWidth = 800, maxHeight = 800,
// 目标尺寸
targetWidth = originWidth, targetHeight = originHeight;
// 图片尺寸超过500x500的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
targetWidth = maxWidth;
targetHeight = Math.round(maxWidth * (originHeight / originWidth));
} else {
targetHeight = maxHeight;
targetWidth = Math.round(maxHeight * (originWidth / originHeight));
}
}
// canvas对图片进行缩放
canvas.width = targetWidth;
canvas.height = targetHeight;
// 清除画布
context.clearRect(0, 0, targetWidth, targetHeight);
// 图片压缩
context.drawImage(img, 0, 0, targetWidth, targetHeight);
return new Promise((resolve) => {
canvas.toBlob((blob) => {
resolve(blob)
}, file.file.type || 'image/png');
}).catch((err) => {
console.log(err);
})
}
/**
* 上传单张图片
* @param {Object} file 图片 类型为 File
* @param {} bucketType 存储类型
* @param {*} options
* @returns 图片的链接
*/
const uploadOneFile = async function (file, bucketType, options) {
let suffix = file.file.name.slice(file.file.name.lastIndexOf('.') + 1), // 获取文件后缀
param = {
bucketType: bucketType, // 存储类型
Suffix: suffix, // 文件后缀
accessKey: '*******',
}
// 获取服务器临时访问权限. 从STS服务获取的临时访问凭证,临时访问凭证包括临时访问密钥(AccessKeyId和AccessKeySecret)和安全令牌(SecurityToken)。
let res = await this.$httpRequest('你自己的URL', param); // 此处可换成你的Ajax请求,获取对应的凭证
if (res.code !== 200) {
console.log('获取阿里云OSS服务临时上传权限接口报错');
throw new Error(res);
}
const { data } = res.data;
const fileName = data.objectKey; // 后台生成的文件名
const client = new OSS({
region: 'oss-ap-southeast-1', // 申请OSS服务所在的地域
endpoint: data.endpoint, // 访问域名
accessKeyId: data.token.AccessKeyId, // 标识用户
accessKeySecret: data.token.AccessKeySecret, // 用户用于加密签名字符串和OSS用来验证签名字符串的密钥
bucket: data.bucket, // 存储空间
stsToken: data.token.SecurityToken, // 安全令牌
secure: true,
});
// 压缩图片
let compressFile = await compressImg(file);
return new Promise(async (resolve, reject) => {
try {
let res = await client.multipartUpload(fileName, compressFile, options);
resolve(res.res.requestUrls[0].split('?')[0]);
} catch (err) {
console.log(err);
}
});
}
上传成功后:
图片的地址:文件名格式说明
例子:
以下例子是我使用上面的封装修改成H5的,可以使用云账号直接上传,一般我们都是使用请求接口获取临时权限才上传:
踩坑点:
- 2019.9月份后新建的bucket存储的图片,直接访问,会直接下载,如果想直接访问,有一种方式是配置域名,可能还有其他的。
- 使用服务去访问上传的图片,会被拒绝,直接浏览器就可以。
- 跨域配置,在bucket中配置, 刚开始设置,会有点延迟生效 _!!!,一直以为是我的问题 ,原来有点延迟,清一下缓存。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 引入oss的sdk -->
<script src="http://gosspublic.alicdn.com/aliyun-oss-sdk-6.0.0.min.js"></script>
</head>
<body>
<input id="file" type="file">
<img src="" id="uploadImg">
</body>
<script>
/**
*
* @param {图片地址} url
* @returns
*/
const loadImage = function (url) {
return new Promise((resolve, reject) => {
let img = new Image();
img.src = url;
img.onload = () => resolve(img);
img.onerror = reject;
})
}
/**
* 压缩图片、减少带宽
* @param {*} file
* @returns
*/
const compressImg = async function (file) {
// 建立Canvas对象(画布)
let canvas = document.createElement('canvas'),
// 获取对应的CanvasRenderingContext2D对象(画笔)
context = canvas.getContext('2d'),
img = await loadImage(file.content),
// 图片原始尺寸
originWidth = img.width,
originHeight = img.height,
// 最大尺寸限制
maxWidth = 800,
maxHeight = 800,
// 目标尺寸
targetWidth = originWidth,
targetHeight = originHeight;
// 图片尺寸超过500x500的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
targetWidth = maxWidth;
targetHeight = Math.round(maxWidth * (originHeight / originWidth));
} else {
targetHeight = maxHeight;
targetWidth = Math.round(maxHeight * (originWidth / originHeight));
}
}
// canvas对图片进行缩放
canvas.width = targetWidth;
canvas.height = targetHeight;
// 清除画布
context.clearRect(0, 0, targetWidth, targetHeight);
// 图片压缩
context.drawImage(img, 0, 0, targetWidth, targetHeight);
return new Promise((resolve) => {
canvas.toBlob((blob) => {
resolve(blob)
}, file.type || 'image/png');
}).catch((err) => {
console.log(err);
})
}
/**
* 上传单张图片
* @param {Object} file
* @param {} bucketType 存储类型
* @param {*} options
* @returns 图片链接
*/
const uploadOneFile = async function (file, bucketType, options) {
/**
* 1. 此处可以Ajax请求获取服务器临时访问权限. 从STS服务获取的临时访问凭证,临时访问凭证包括临时访问密钥
* (AccessKeyId和AccessKeySecret)和安全令牌(SecurityToken)。
* 2. 如果你有自己的服务器,也可以拿永久的来尝试看看,正常应该使用临时访问权限
*/
const client = new OSS({
region: '******', // 申请OSS服务所在的地域
accessKeyId: '******', // 标识用户
accessKeySecret: '*****', // 用户用于加密签名字符串和OSS用来验证签名字符串的密钥
bucket: 'uploade', // 存储空间
secure: true,
});
let compressFile = await compressImg(file);
return new Promise(async (resolve, reject) => {
try {
let res = await client.multipartUpload(file.name, compressFile);
resolve(res.res.requestUrls[0].split('?')[0]);
} catch (err) {
console.log(err);
}
});
}
const add = () => {
let inputFile = document.getElementById("inputFile"),
uploadImg = document.getElementById("uploadImg")
eleFile = document.querySelector('#file'), reader = new FileReader();
eleFile.addEventListener('change', (event) => {
file = event.target.files[0];
// 选择的文件是图片
if (file.type.indexOf("image") == 0) {
// 这里主要是从系统中获取上传的文件转成base64位,正常我们使用现成的ui库,如vant,是可以直接拿到base64位的图片地址
reader.readAsDataURL(file);
reader.onload = async (e) => {
file.content = e.target.result;
let res = await uploadOneFile(file);
uploadImg.src = res;
console.log(res);
};
}
});
}
add();
</script>
</html>
尝试上传 85.6 KB的图片
浏览器:
使用服务访问:
直接访问:
oss客户端查看我上传的图片:可以看到图片被压缩了,变成了30.48kb