1.oss上传插件安装
插件地址插件安装教程
安装成功如图所示:
上传视频的话需要再根据需求更改一下配置文件,更改如下
const fileType=filePath.split('.')[1]
const aliyunFileKey = dir + new Date().getTime() + Math.floor(Math.random() * 150) +`.${fileType}`;
如图所示:
2.安装图像转换工具
插件地址 安装成功如图所示:
2.组件封装新建UploaderImage/index.vue文件
<template>
<view>
<!-- vant 上传组件,用来图片多选 -->
<van-uploader
class="upload-border"
:accept="accept"
:file-list="fileList"
:deletable="!disabled"
:disabled="disabled"
@after-read="_afterRead"
@delete="deleteImg"
:multiple="true"
:max-count="maxCount"
:capture="capture"
image-fit="none"
:upload-icon="uploadIcon"
></van-uploader>
<view style="position: absolute;top: -999999px;"> //把canvas移出视窗
<view>
<canvas :style="{'width':canvasWidth+'px','height': canvasHeight+'px'}" canvas-id="myCanvas"></canvas>
</view>
</view>
</view>
</template>
<script>
import uploadImage from '@/js_sdk/yushijie-ossutil/ossutil/uploadFile.js';
// 以下路径需根据项目实际情况填写
import { pathToBase64, base64ToPath } from '@/js_sdk/image-tools/index.js';
export default {
props: {
// 是否禁用
disabled: {
type: Boolean,
default: false
},
// 上传最大张数
maxCount: {
type: Number,
default: 4
},
// 是否上传至oss
isUploaderOss: {
type: Boolean,
default: false
},
// 图片数组
photoList: {
type: Array,
default: () => []
},
// 图片或者视频选取模式
capture: {
type: Array,
default: () => {
return ['album', 'camera'];
}
},
// 接受的文件类型, 可选值为all media image file video
accept: {
type: String,
default: 'image'
},
// 图片上传到 OSS 服务上的存储目录
ossPathPrefix: {
type: String,
default: 'yzom_img/APPLET/'
},
// 上传图标
uploadIcon: {
type: String,
default: 'photograph'
},
// 是否需要压缩
isCompress: {
type: Boolean,
default: true
},
// 文件上传大小限制,单位 byte
compressMaxSize: {
type: [Number, String],
default: 1024 * 1024
},
// 图片压缩质量,0~100,数值越小,质量越低,压缩率越高
quality: {
type: Number,
default: 80
},
// 图片是否添加水印
isAddWaterMark: {
type: Boolean,
default: true
}
},
data() {
return {
fileQuality: null,
canvasWidth: '',
canvasHeight: ''
};
},
computed: {
fileList: {
get() {
// console.log('photoList---', this.photoList);
return this.photoList;
},
set(val) {
this.$emit('uploadSuccess', val);
}
}
},
methods: {
_afterRead(event) {
const { file } = event.detail;
// console.log('file---', file);
// 图片要进行压缩及添加水印
if (this.accept === 'image') {
this.afterRead(file);
} else {
this.afterReadVideo(file);
}
},
// 视频上传视频只用进行压缩
async afterReadVideo(file) {
const files = [];
for (let i = 0; i < file.length; i++) {
const ele = file[i];
// files.push(this.addWaterMark(ele.url));
if (this.isCompress) {
await this.videoCompress(ele).then(res => {
// console.log('压缩返回---', res);
files.push(res);
});
} else {
files.push(ele);
}
Promise.all(files).then(res => {
console.log('返回成功', res);
uni.hideLoading();
const that = this;
uni.showLoading({
title: '上传中...'
});
this.uploadImage(res);
});
}
},
// 图片上传
async afterRead(file) {
const files = [];
// console.log(file);
this.fileQuality = this.quality;
for (let i = 0; i < file.length; i++) {
const ele = file[i];
// files.push(this.addWaterMark(ele.url));
if (this.isCompress) {
if (this.isAddWaterMark) {
// 添加水印返回
await this.addWaterMark(ele.url).then(res => {
// console.log('添加水印返回---', res);
files.push(res);
});
} else {
// 压缩返回
await this.imgCompress(ele).then(res => {
// console.log('压缩返回---', res);
files.push(res);
});
}
} else {
if (this.isAddWaterMark) {
await this.addWaterMark(ele.url).then(res => {
files.push(res);
});
} else {
files.push(ele);
}
}
}
Promise.all(files).then(res => {
// console.log('返回成功', res);
uni.hideLoading();
const that = this;
uni.showLoading({
title: '上传中...'
});
this.uploadImage(res);
});
},
// 上传图片
uploadImage(file) {
const that = this,
uploadPromiseTask = [], // 定义上传的promise任务栈,线上图片
filePromiseTask = []; // 本地图片
this.$emit('afterRead', file);
for (let i = 0; i < file.length; i++) {
if (this.isUploaderOss) {
// push进每一张所需要的上传的图片promise栈
uploadPromiseTask.push(that.uploadFile(file[i].url));
} else {
// push进每一张所需要的上传的图片promise栈
uploadPromiseTask.push(file[i]);
}
}
Promise.all(uploadPromiseTask)
.then(res => {
that.fileList = that.fileList.concat(res);
// console.log('fileList', that.fileList);
uni.hideLoading();
})
.catch(err => {
console.log(err);
// 存在有上传失败的文件
uni.hideLoading();
uni.showToast({
title: '上传失败!',
icon: 'none'
});
});
},
// 上传文件到线上
uploadFile(uploadFile) {
return new Promise((resolve, reject) => {
const url = 'test';
uploadImage(
uploadFile,
this.ossPathPrefix + url + '/',
// 上传成功
result => {
// console.log(result)
resolve({
url: result
});
},
// 上传失败
result => {
uni.showToast({
title: '上传失败',
icon: 'none'
});
// 这里写上传失败的代码
// console.log(JSON.stringify(result));
}
);
});
},
// 删除
deleteImg(event) {
const delIndex = event.detail.index,
fileList = this.fileList;
fileList.splice(delIndex, 1);
this.fileList = fileList;
this.$emit('uploadSuccess', this.fileList);
},
// 相机手动拍照上传
getCamera() {
const that = this;
uni.chooseImage({
count: that.maxCount, // 最多可以选择的图片张数,默认9
sizeType: ['original', 'compressed'], // original 原图,compressed 压缩图,默认二者都有
sourceType: that.capture, // album 从相册选图,camera 使用相机,默认二者都有。如需直接开相机或直接选相册,请只使用一个选项
// 成功则返回图片的本地文件路径列表 tempFilePaths
success: function(res) {
const file = res.tempFiles,
fileList = [];
for (let i = 0; i < file.length; i++) {
const element = file[i],
params = {
url: element.path,
size: element.size
};
fileList.push(params);
}
that.afterRead(fileList);
}
});
},
// 图片压缩
imgCompress(file) {
return new Promise((resolve, reject) => {
if (file.size > this.compressMaxSize) {
this._imgCompress(file, resolve, reject);
} else {
// console.log('compressMaxSize', file.size > this.compressMaxSize);
resolve(file);
}
});
},
_imgCompress(file, resolve, reject) {
uni.showLoading({
title: '正在压缩图片'
});
const that = this;
uni.compressImage({
src: file.url,
quality: that.fileQuality, // 仅对jpg有效
width: '50%',
success: res => {
// console.log('compressImage', res);
// 文件转base64
pathToBase64(res.tempFilePath)
.then(base64 => {
const imgSize = that.imageSize(base64) * 1024,
params = {
url: file.url,
size: imgSize
};
if (imgSize > that.compressMaxSize) {
uni.hideLoading();
// console.log('fileQuality', that.fileQuality);
if (that.fileQuality < 10) {
uni.showToast({
icon: 'none',
title: '图片过大,请重新选择',
duration: 5000
});
} else {
that.fileQuality = (that.fileQuality - 20);
that._imgCompress(params, resolve, reject);
}
} else {
uni.showToast({
icon: 'none',
title: '压缩成功'
});
resolve(params);
}
console.log('最终图片大小:', imgSize / 1024, 'KB');
})
.catch(error => {
console.error(error);
uni.showToast({
icon: 'none',
title: '转换失败'
});
});
},
fail: err => {
uni.showToast({
icon: 'none',
title: '压缩失败'
});
reject(err);
},
complete: () => {}
});
},
// base64 获取文件大小
imageSize(base64Str) {
const tag = 'base64,',
indexBase64 = base64Str.indexOf(tag);
if (indexBase64 < 0) return -1;
const str = base64Str.substr(indexBase64 + tag.length),
// 计算文件流大小KB
// const size = ((str.length - (str.length / 8) * 2) / 1024).toFixed(2) + 'KB'
size = ((str.length * 0.75) / 1024).toFixed(2);
return size;
},
// 添加水印
addWaterMark(filePath) {
return new Promise((resolve, reject) => {
const that = this;
uni.showLoading({
icon: 'none',
title: '正在添加水印'
});
uni.getImageInfo({// 获取图片信息
src: filePath,
success: res => {
that.canvasWidth = res.width;
that.canvasHeight = res.height;
// var ctx = uni.createCanvasContext('myCanvas'),
// 在自定义组件内 需要传递第二参数 this canvas才生效
var ctx = uni.createCanvasContext('myCanvas', this);
ctx.clearRect(0, 0, res.width, res.height);// 在给定的矩形内清除指定的像素。
ctx.beginPath();// 起始一条路径,或重置当前路径
ctx.drawImage(filePath, 0, 0, res.width, res.height);// 向画布上面绘制图片
// 为图片添加水印
ctx.translate(0, 0);// 重新映射画布上的 (0,0) 位置
// 这部分是水印的大小位置和数量
let imgWidth = '',
imgHeight = '';
if (res.width > res.height) {
imgWidth = res.width;
imgHeight = res.height;
} else {
imgWidth = res.height;
imgHeight = res.width;
}
// 水印的字体大小何位置计算
const fonstsize = (imgWidth / 40).toFixed(2),
textToWidth = imgWidth / 5 * 0.5,
textToHeight = (imgHeight / 8).toFixed(2) * 0.5;
// console.log('fonstsize---', fonstsize);
// console.log('textToWidth---', textToWidth);
// console.log('textToHeight---', textToHeight);
// 时间水印
let str = `时间:${new Date()}`,
lineWidth = fonstsize,
// 文本绘制的初始高度
initHeight = textToHeight,
// 绘制文本的高度
titleHeight = 100;
// 当前地点水印
str = str + `\r\n定位地点:当前位置`;
// 执行的工单类型水印
str = str + `\r\n工单类型:报修工单`;
ctx.setFillStyle('#ff0000');// 水印字体颜色
const fontSize1 = (fonstsize < 16 ? 16 : fonstsize);
ctx.setFontSize(fontSize1);// 水印字体大小
ctx.setTextAlign('left');// 水印字体位置
var textArray = str.split('\r\n');
// console.log(textArray);
// 对水印字符进行转行字符高度等判断(以此适配图片)
for (var j = 0; j < textArray.length; j++) {
var item = textArray[j],
isHeight = that.drawText(ctx, item, initHeight, titleHeight, textToHeight),
divideHeight = isHeight.split(',');
initHeight = Number(divideHeight[0]) + textToHeight;
titleHeight = Number(divideHeight[1]) + textToHeight;
}
// 开始绘制添加水印的图片并显示在页面中
ctx.draw(false, () => {
setTimeout(() => {
// canvas转文件
uni.canvasToTempFilePath({
x: 0,
y: 0,
width: that.canvasWidth, // 画布宽度
height: that.canvasHeight, // 画布高度
destWidth: that.canvasWidth, // 输出图片宽度
destHeight: that.canvasHeight, // 输出图片高度
canvasId: 'myCanvas',
fileType: 'jpg', // 目标文件的类型,只支持 'jpg' 或 'png'。默认为 'png'
quality: 0.5, // 图片的质量,取值范围为 (0, 1],不在范围内时当作1.0处理(如果不对此进行压缩,会导致图片很大)
success: res => {
uni.hideLoading();
// 注意此时的地址是加了水印的图片地址(直接url输入浏览器也可以查看包含水印)
// console.log('水印添加成功成功---', res);
// 文件转base64
pathToBase64(res.tempFilePath)
.then(base64 => {
const imgSize = that.imageSize(base64) * 1024,
params = {
size: imgSize,
url: res.tempFilePath
};
console.log('最终图片大小:', (imgSize / 1024 / 1024).toFixed(2), 'M');
if (that.isCompress) {
that.imgCompress(params, resolve, reject).then(res => {
resolve(res);
}).catch((err) => {
reject(err);
});
} else {
resolve(params);
}
})
.catch(error => {
uni.showToast({
title: '文件转换失败',
icon: 'none'
});
console.error(error);
});
}
// });
// 在自定义组件内 需要传递第二参数 this canvas才生效
}, this);
}, 500);
});
},
fail: (e) => {
console.log(e);
uni.hideLoading();
uni.showToast({
title: '水印合成失败',
icon: 'none'
});
}
});
});
},
// canvans绘制多行文本,自动转行
/*
item:字符串,
initHeight:初始高度
*/
drawText: function(ctx, item, initHeight, titleHeight, textToHeight) {
var lineWidth = 0,
// 每次开始截取的字符串的索引
lastSubStrIndex = 0;
for (var i = 0; i < item.length; i++) {
lineWidth += ctx.measureText(item[i]).width;
if (lineWidth > (this.canvasWidth - textToHeight)) {
// 绘制截取部分
ctx.fillText(item.substring(lastSubStrIndex, i), textToHeight, initHeight);
initHeight += textToHeight;
lineWidth = 0;
lastSubStrIndex = i;
titleHeight += textToHeight;
}
// 绘制剩余部分
if (i == item.length - 1) {
ctx.fillText(item.substring(lastSubStrIndex, i + 1), textToHeight, initHeight);
}
}
// console.log('结束一轮之后的initheight: ' + initHeight + ' titleHeight: ' + titleHeight);
return initHeight + ',' + titleHeight;
},
// 视频压缩
videoCompress(file) {
console.log(file);
return new Promise((resolve, reject) => {
if (file.size > this.compressMaxSize) {
this._videoCompress(file, resolve, reject, 'medium');
} else {
// console.log('compressMaxSize', file.size > this.compressMaxSize);
resolve(file);
}
});
},
_videoCompress(file, resolve, reject, quality) {
uni.showLoading({
title: '正在压缩视频...'
});
var that = this;
uni.compressVideo({
src: file.url,
quality: quality, // 'low':低,'medium':中,'high':高
success: res => {
console.log('压缩后', res);
uni.showToast({
title: '视频压缩成功',
icon: 'none'
}, 2000);
console.log('压缩后大小---', res.size / 1024 + 'M');
const params = {
url: res.tempFilePath,
size: res.size
};
// 压缩后的大小仍大于所设大小继续压缩
if ((res.size * 1024) > this.compressMaxSize) {
if (quality === 'medium') {
that._videoCompress(file, resolve, reject, 'low');
} else {
uni.showToast({
icon: 'none',
title: '视频太大,请重新选择'
});
resolve(params);
}
} else {
resolve(params);
}
},
fail: err => {
uni.showToast({
title: '视频压缩失败',
icon: 'none'
}, 2000);
console.log('error---', err);
reject(err);
}
});
}
}
};
</script>
<style scoped lang="scss">
</style>
3.组件使用新建imgTest/index.vue
代码如下:
<template>
<view class="img-test">
图片上传测试
<uploader-image
:ossPathPrefix="'test/yzom_img/APPLET/task_dtl/'"
:photoList="phototList"
:isUploaderOss="true"
:maxCount="3"
:ref="oploaderOss"
:upload-icon="'photograph'"
@uploadSuccess="uploadImageSuccess"
/>
视频上传测试
<!-- 上传视频不能超过4M -->
<uploader-image
:compressMaxSize="1024 * 1024 * 4"
:ossPathPrefix="'yzom_img/APPLET/task_dtl/'"
:photoList="vidoeList"
:isUploaderOss="true"
:maxCount="1"
:accept="'video'"
ref="oploaderOss "
:upload-icon="'play-circle'"
@uploadSuccess="uploadVideoSuccess"
/>
</view>
</template>
<script>
import UploaderImage from '@/components/UploaderImage';
export default {
name: 'ImgTest',
components: { UploaderImage },
data() {
return {
phototList: [],
vidoeList: []
};
},
onLoad() {
},
methods: {
// 图片上传成功返回
uploadImageSuccess(fileList) {
console.log('fileList----', fileList);
this.phototList = fileList;
},
// 图片上传成功返回
uploadVideoSuccess(fileList) {
console.log('fileList----', fileList);
this.vidoeList = fileList;
}
}
};
</script>
<style scoped lang="scss">
.img-test{
padding:15rpx;
}
</style>
上传成功如图所示
放大图:
图片添加水印参考链接图片添加水印文本超出自动换行参考链接
视频压缩报错如下
解决参考链接