功能需求
- 可以拍照、单张/批量选择图片:因为应用场景是在微信小程序里边,所以单次选择图片最多9张,可以多次选择图片,图片总数量不收限制;
- 对选择的图片(批量)压缩:使用 Canvas 对图片进行重绘制再压缩;
- 显示压缩后的预览图:在页面上显示压缩后的图片;
- 删除图片;
- 最后批量上传:全部图片选择完成后,统一上传操作;
- 上传图片远程地址:图片上传完成之后会返回图片在服务端的远程地址,将图片远程地址上传至服务端;
实现逻辑: 选择图片、压缩、预览、删除等功能全部在本地处理,这样做可以减少请求,且用户体验比较好,响应迅速;在上传之前做删除操作,可以避免服务端存储太多垃圾图片文件;
接口示例:
- 图片上传接口(POST):upload.image.io
- 远程地址接口(POST):remote.image.io
小二儿,上代码~
<!-- Canvas 结构并绝对定位于视窗范围之外 -->
<!-- 这里不可对 Canvas 结构设置隐藏,否则在绘制图片时无法获取图片 -->
<canvas canvas-id="canvas" style="width:{{canvasWidth}}px;height:{{canvasHeight}}px;position:absolute;left:-1000%;top:-1000%;"></canvas>
Page({
/**
* 页面的初始数据
*/
data: {
localImageArr: [], // 本地图片数组
remoteImageArr: [], // 远程图片数组
canvasWidth: '', // 当前图片的宽度
canvasHeight: '', // 当前图片的高度
uploadTotal: 0 // 上传成功的图片数
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
},
/**
* 查看图片
*/
previewImage: function(e) {
wx.previewImage({
current: e.currentTarget.dataset.src, // 当前显示图片的http链接
urls: this.data.localImageArr // 需要预览的图片http链接列表
})
},
/**
* 添加答案图片,并压缩图片
*/
addImage: function() {
wx.chooseImage({
count: 9, // 设置单次选择图片的最大数量:1~9
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: res => {
// 压缩图片
this.compressImage(0, res.tempFilePaths);
}
})
},
/**
* 压缩图片
*/
compressImage: function(index, tempImageArr) {
wx.showLoading({
title: '图片加载中',
mask: true
})
if(index < tempImageArr.length) {
wx.getImageInfo({
src: tempImageArr[index],
success: info => {
// 获取当前图片尺寸,用于设置 Canvas 尺寸
this.setData({
canvasWidth: info.width,
canvasHeight: info.height
})
// 延迟 100ms 绘制图片,避免首张图片绘制为空白图片
setTimeout(() => {
const ctx = wx.createCanvasContext('canvas');
ctx.drawImage(tempImageArr[index], 0, 0, info.width, info.height);
ctx.draw(false, () => {
index = index + 1;
wx.canvasToTempFilePath({
canvasId: 'canvas',
fileType: 'jpg',
quality: 0.6,
success: image => {
this.data.localImageArr.push(image.tempFilePath);
this.setData({
localImageArr: this.data.localImageArr
})
this.compressImage(index, tempImageArr);
},
fail: err => {
console.log(err)
}
})
})
}, 100);
}
})
} else {
wx.hideLoading();
}
},
/**
* 删除图片
*/
delImage: function(e) {
let index = e.currentTarget.dataset.index;
this.data.localImageArr.splice(index, 1);
this.setData({
localImageArr: this.data.localImageArr
})
},
/**
* 上传图片
*/
uploadImage: function(tempImage) {
return new Promise((resolve, reject) => {
wx.uploadFile({
url: 'upload.image.io',
filePath: tempImage,
name: 'file',
formData: {
signature: '57fa0b604017e386586fef59ed13e3a10ebcf925' // 签名
},
success: res => {
let body = res.data;
let data = body.data;
if(res.statusCode == 200) {
// 将图片远程地址追加至数组
this.data.remoteImageArr.push(data.filePath);
this.setData({
uploadTotal: this.data.uploadTotal + 1,
remoteImageArr: this.data.remoteImageArr
})
// 返回图片远程地址
resolve(data.filePath)
} else {
wx.showModal({
title: '',
content: body.message,
confirmText: '重试',
confirmColor: '#4266f6',
success: res => {
if(res.confirm) {
this.uploadImage();
} else if(res.cancel) {
wx.hideToast();
}
}
})
console.log(body.message);
}
}
})
})
},
/**
* 提交图片
*/
submitImage: function() {
if(this.data.localImageArr.length == 0) {
wx.showToast({
title: '请至少添加一张照片',
icon: 'none',
duration: 2000
})
return false;
}
wx.getNetworkType({
success: res => {
// 提交之前获取网络状态
if(res.networkType == 'unknown' || res.networkType == 'none') {
wx.showToast({
title: '请求失败,请检查网络连接',
icon: 'none',
duration: 2000
})
} else {
wx.showModal({
title: '提示',
content: '确认提交?',
confirmColor: '#4266f6',
success: res => {
if(res.confirm) {
wx.showLoading({
title: '上传中',
mask: true
})
// 触发图片上传操作,并返回远程图片地址数组
// 返回远程图片地址数组(remoteImageArr)与本地变量保存的远程图片数组(this.data.remoteImageArr)的区别数,本地变量数组是无序的,返回的数组是根据页面预览排序的顺序进行存储数据的
// 所以在提交远程图片地址的时候建议使用返回的远程图片数组作为参数提交
const promises = this.data.localImageArr.map(tempImage => {
return this.uploadImage(tempImage);
});
// 等待图片上传完成之后,再执行远程地址上传至服务器操作
Promise.all(promises).then(remoteImageArr => {
wx.request({
url: 'remote.image.io',
data: {
signature: '57fa0b604017e386586fef59ed13e3a10ebcf925', // 签名
answerImgs: JSON.stringify(remoteImageArr) // 返回的远程图片地址数组
},
header: {
"Content-Type": "application/x-www-form-urlencoded"
},
method: "POST",
success: res => {
wx.hideLoading();
let body = res.data;
let data = body.data;
if(res.statusCode == 200) {
wx.showToast({
title: '上传成功',
icon: 'success',
duration: 2000,
success: res => {
wx.navigateBack({
delta: 1
})
}
})
} else {
wx.showToast({
title: body.message,
icon: 'none',
duration: 2000
})
console.log(body.message);
}
},
fail: err => {
wx.showToast({
title: '提交答案接口错误',
icon: 'none',
duration: 2000
})
console.log(err);
}
})
}).catch(function(reason){
// ...
});
} else if(res.cancel) {
wx.hideToast();
}
}
})
}
}
})
}
})