uniapp压缩图片的api,uni.compressimage在h5环境暂不支持,详见官网-平台差异说明

所以我们在这件事情上,需要超越uniapp官方。诶 听起来还不错

一、选图片

代码里面有一些vk.的操作,这是个2022年出来的云开发框架,vk的特点是让写JS的前端三天内变成全栈。有兴趣可以戳链接了解,vk的作者还坑过我一次害我22点饿着肚子在加班。vk框架秒变全栈

Q:为什么压缩(that.compressImage)和上传(that.uploadImg)都用到Promise.all()?
因为我们是支持批量上传,当选择10张图片时,我们没有理由压缩完一张再压缩下一张,JS默认是单线程执行代码。所以我们需要优化,面试时聊得最多的优化。
uploadFile() {
    let type = that.queryForm1.formData.type;
    let fileType;
    let extension = [];
    if (type === "image") {
        extension = ["png", "jpg", "jpeg", "gif", "bmp", "svg"];
        fileType = "image";
    } else if (type === "video") {
        extension = ["avi", "mp3", "mp4", "3gp", "mov", "rmvb", "rm", "flv", "mkv"];
        fileType = "video";
    } else if (type === "other") {
        extension = ["txt", "pdf", "xls", "xlsx", "ppt", "pptx", "doc", "docx", "rar", "zip"];
    }
    const tasksUload = [];
    const tasksCompress = [];
    uni.chooseFile({
        extension,
        success: res => {
            vk.showLoading("上传中...");
            const {
                tempFilePaths,
                tempFiles
            } = res
           
            tempFiles.forEach((file, index) => {
                console.log(`图片大小:${file.size}====图片名称:${file.name}====类型${file.type}`)
                const filePath = tempFilePaths[index]
                // 图片大于1MB进行压缩
                if (fileType == 'image' && file.size / 1024 > 1025) {
                    tasksCompress.push(that.compressImage(file))
                } else {
                    tasksUload.push(that.uploadImg(file, fileType))
                }
            })

            if (tasksCompress.length > 0) {
                Promise.all(tasksCompress).then(files => {
                    // 压缩完毕,拿到压缩后的图片文件数组files
                    files.forEach(file => tasksUload.push(that.uploadImg(file, fileType)))
                    uploadFu()
                })
            } else {
                uploadFu()
            }
        }
    });
    function uploadFu() {
        Promise.all(tasksUload)
            .then(res1 => {
                vk.hideLoading();
                that.getList();// 自己的查询接口
            })
            .catch(err => {
                vk.hideLoading();
                console.error(err);
            });
    }
},

二、压缩图片

以下是传统前端压缩思路(应该还有更高级的,可我不会,希望有大佬来评论区分享下)

  1. uni.getImageInfo得到图片的信息

  1. 把图片画到cavans上,这一步图片已经被压缩

  1. 使用cavans的toDataURL方法得到base64文件流的格式

  1. 因为后端懒得处理base64二进制,所以我们前端要把base64二进制再转成File文件格式

Q:什么是base64二进制?
它是以data:image/png;base64开头的格式,png也可能是jpg、svg等图片类型。
它是包括小写字母a-z、大写字母A-Z、数字0-9、符号"+"、"/"一共64个字符的字符集。
更详细讲解

Q:为什么png图片格式不能压缩,需要转成jpge?
因为 canvas 是不支持无损的 png 压缩的。如果我们要进行压缩,那就需要转为 jpeg ,同时 png 的透明效果将会消失。

// 压缩图片
compressImage(urlData) {
    vk.showLoading("压缩中...");
    const asyncFunc = new Promise(resolve => {
        uni.getImageInfo({
            src: urlData.path,
            success(res) {
                let originWidth = res.width; //图片原始宽
                let originHeight = res.height; //图片原始高
                let img = new Image()
                img.src = res.path
                let canvas = document.createElement('canvas');
                let ctx = canvas.getContext('2d')
                // 目标尺寸
                let targetWidth = originWidth;
                let targetHeight = originHeight;
                canvas.width = targetWidth
                canvas.height = targetHeight
                // 图片压缩
                ctx.drawImage(img, 0, 0, targetWidth, targetHeight)
                // 将png格式文件转为jepg输出,因为png图片不能用这种方式进行压缩
                let filetype = urlData.type === 'image/png' ? 'image/jpeg' : urlData.type
                // canvas对图片进行缩放 0.5是我定义的图片质量
                let base64 = canvas.toDataURL(filetype, 0.5);
                // 将base64转换为filel流
                let flie = that.convertBase64UrlToFile({
                    dataURL: base64,
                    type: urlData.type,
                    contentName: urlData.name
                })
                console.log('压缩后', flie)

                resolve(flie)
            },
            fail() {
                uni.showModal({
                    title: '提示',
                    content: '图片压缩失败',
                    showCancel: false
                });
            }
        })
    })
    return asyncFunc
},

三、base64二进制转成File文件格式

说实话这段代码比较底层,我是看不太懂的,可如果我们看懂了,面试时我们便是见多识广,游刃有余,天下无敌

Q:window.atob()是什么?
btoa():从二进制数据“字符串”创建一个 Base-64 编码的 ASCII 字符串(“btoa”应读作“binary to ASCII”)
atob():解码通过 Base-64 编码的字符串数据(“atob”应读作“ASCII to binary”)
// base64转成File文件格式
convertBase64UrlToFile(base64) {
    let urlData = base64.dataURL
    let type = base64.type
    let contentName = base64.contentName
    let bytes = null
    if (urlData.split(',').length > 1) { //是否带前缀
        bytes = window.atob(urlData.split(',')[1]) // 去掉url的头,并转换为byte
    } else {
        bytes = window.atob(urlData)
    }
    // 处理异常,将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)
    }
    let result = new Blob([ab], {
        type: type,
    })
    let result1 = new File([result], contentName, {
        type: type
    })
    result1.path = window.URL.createObjectURL(result)
    return result1
},

四、请求接口上传图片

我这里是vk的api方法,大家换成自己的axios请求就好

// 上传图片到云存储
uploadImg(file, fileType) {
    const asyncUload = new Promise(function(resolve, reject) {
        vk.callFunctionUtil.uploadFile({
            filePath: file.path,
            file: file,
            needSave: true,
            fileType,
            category_id: that.queryForm1.formData.category_id,
            addSuccess: function(res) {
                // 等保存到数据库后才执行resolve
                resolve(res);
            }
        });
    })
    return asyncUload
},

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐