小程序批量选择、压缩、预览、上传图片功能

功能需求

  1. 可以拍照、单张/批量选择图片:因为应用场景是在微信小程序里边,所以单次选择图片最多9张,可以多次选择图片,图片总数量不收限制;
  2. 对选择的图片(批量)压缩:使用 Canvas 对图片进行重绘制再压缩;
  3. 显示压缩后的预览图:在页面上显示压缩后的图片;
  4. 删除图片;
  5. 最后批量上传:全部图片选择完成后,统一上传操作;
  6. 上传图片远程地址:图片上传完成之后会返回图片在服务端的远程地址,将图片远程地址上传至服务端;

实现逻辑: 选择图片、压缩、预览、删除等功能全部在本地处理,这样做可以减少请求,且用户体验比较好,响应迅速;在上传之前做删除操作,可以避免服务端存储太多垃圾图片文件;

接口示例:

  • 图片上传接口(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();
                            }
                        }
                    })
                }
            }
        })
    }
})

小程序代码片段:https://developers.weixin.qq.com/s/0BZXAYmY7V6z

发表评论
* 昵称
* Email
* 网址
* 评论