1、初始化OSS
2、获取上传的图片
3、长传图片
1:如果用户上传的图片过大,要进行微损压缩图片
2:如果苹果手机旋转拍照上传图片会造成旋转错位(兼容性处理)
项目采用vant,封装一个直传阿里oss的功能,代码为:
/**
* Created by SongPeng on 2019-8-9
*/
import OSS from 'ali-oss'
import { getOssUploadRole } from '@/api/wallet'
import { Toast, Dialog } from 'vant'
import { ossUrl } from '@/config'
import EXIF from 'exif-js'
/**
* 图片压缩 返回base64
* @param {*base64} base64URL
*/
export function compressImage (base64URL) {
return new Promise((resolve) => {
let orientation = ''
let img = new Image()
let canvas = document.createElement('canvas')
let ctx = canvas.getContext('2d')
img.src = base64URL
img.onload = () => {
// 图片原始尺寸
let originWidth = img.width
let originHeight = img.height
// 最大尺寸限制
let maxWidth = img.width / 2
let maxHeight = img.height / 2
// 目标尺寸
// 图片尺寸超过1000x1000的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
img.width = maxWidth
img.height = Math.round(maxWidth * (originHeight / originWidth))
} else {
img.height = maxHeight
img.width = Math.round(maxHeight * (originWidth / originHeight))
}
}
// canvas对图片进行缩放
canvas.width = img.width
canvas.height = img.height
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height)
EXIF.getData(img, function () {
if (navigator.userAgent.match(/iphone/i)) {
orientation = EXIF.getTag(this, 'Orientation')
switch (Number(orientation)) {
case 2:
ctx.translate(img.width, 0)
ctx.scale(-1, 1)
ctx.drawImage(img, 0, 0, img.width, img.height)
break
case 3:
ctx.rotate(180 * Math.PI / 180)
ctx.drawImage(img, -img.width, -img.height, img.width, img.height)
break
case 4:
ctx.translate(img.width, 0)
ctx.scale(-1, 1)
ctx.rotate(180 * Math.PI / 180)
ctx.drawImage(img, -img.width, -img.height, img.width, img.height)
break
case 5:
ctx.translate(img.width, 0)
ctx.scale(-1, 1)
ctx.rotate(90 * Math.PI / 180)
ctx.drawImage(img, 0, -img.width, img.height, img.width)
break
case 6:
canvas.width = img.height
canvas.height = img.width
ctx.rotate(90 * Math.PI / 180)
ctx.drawImage(img, 0, 0, img.width, -img.height)
break
case 7:
ctx.translate(img.width, 0)
ctx.scale(-1, 1)
ctx.rotate(270 * Math.PI / 180)
ctx.drawImage(img, -img.height, 0, img.height, img.width)
break
case 8:
ctx.rotate(270 * Math.PI / 180)
ctx.drawImage(img, -img.height, 0, img.height, img.width)
break
default:
ctx.drawImage(img, 0, 0, img.width, img.height)
break
}
} else {
ctx.drawImage(img, 0, 0, img.width, img.height)
}
})
let base64 = canvas.toDataURL('image/jpeg', 0.7)
resolve(base64)
}
})
}
/**
* 将以base64的图片url数据转换为Blob
* @param urlData图片base64数据
* @name convertBase64UrlToBlob
*/
export function convertBase64UrlToBlob (urlData) {
let bytes = window.atob(urlData.split(',')[1]) // 去掉url的头,并转换为byte
// 处理异常,将ascii码小于0的转换为大于0
let ab = new ArrayBuffer(bytes.length)
let ia = new Uint8Array(ab)
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i)
}
return new Blob([ab], {
type: 'image/jpg'
})
}
/**
* 将以base64的图片url数据转换为file
* @param dataurl 图片base64数据
* @param filename 图片名称
* @name dataURLtoFile
*/
export function dataURLtoFile (dataurl, filename) { // 将base64转换为文件
let arr = dataurl.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let bstr = atob(arr[1])
let n = bstr.length
let u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], filename, {
type: mime
})
}
const aliOss = {
client: null,
fileName: '',
file: null,
loading: null,
async setClient () {
if (this.client === null) {
const res = await getOssUploadRole()
if (res.code === 0) {
let result = res.data
this.client = new OSS({
accessKeyId: result.accessKeyId,
accessKeySecret: result.accessKeySecret,
stsToken: result.stsToken,
endpoint: result.endpoint,
bucket: result.bucket
})
}
}
},
uploadImg (fileName, file) {
Toast.loading({
mask: true,
duration: 0,
message: '上传中...'
})
return new Promise((resolve, reject) => {
this.fileName = fileName
this.file = file
if (file.file.size / 1024 / 1024 > 0.8) {
compressImage(this.file.content)
.then(base64 => {
this.file.file = convertBase64UrlToBlob(base64)
this.readyLoad(this.fileName, this.file.file)
.then(url => {
resolve(url)
})
})
} else {
this.readyLoad(this.fileName, this.file.file)
.then(url => {
resolve(url)
})
}
})
},
readyLoad (fileName, file) {
return new Promise((resolve, reject) => {
this.client.multipartUpload(fileName, file)
.then(function (result) {
Toast.clear()
Toast({
type: 'success',
message: '上传成功',
duration: 1500
})
resolve(`${ossUrl}/${fileName}`)
})
.catch(function (err) {
Toast.clear()
if (err.indexOf('timeout') !== -1) {
Dialog({
title: '提示',
message: '上传超时'
})
} else {
Dialog({
title: '提示',
message: '上传失败'
})
}
})
})
}
}
export default aliOss
具体使用方法:
import aliOss from '@/utils/uploadImg' // 第一步引入
this.initOss() // 初始化OSS 客户端
onRead (file, options) {
const data = file.file.name.split('.')
let imgType = data[data.length - 1]
let fileName = `img/${options.name}-${this.$store.state.userInfo.id}-${Date.parse(new Date())}.${imgType}`
aliOss.uploadImg(fileName, file)
.then(url => {
// 上传过oss后再同步图片地址到服务器
this.$store.dispatch('brand/setBrandLogo', url)
.then(res => {
if (res.code === 0) {
this.$Toast.success(res.message)
}
})
})
}