问题说明

  1. 只针对uniapp开发H5网页,使用uniapp api获取到的临时路径不能满足使用场景,其他平台未进行测试

使用说明

1.使用场景

使用uview-ui的u-upload组件上传图片

2. 文件需要压缩

我的业务场景是上传图片之后,需要使用ocr识别,文件太大的话,识别比较缓慢,所以需要进行压缩

3. 使用技术

// npm install compressorjs
import Compressor from 'compressorjs';

4. 代码如下

这是在u-upload组件的基础上进行封装的ImageUpload组件, 先获取到临时路径,通过fetch请求获取到blob对象,然后再进行转file处理,相应的处理函数是tempFilePathToFile,compressFile

<template>
    <view class="upload-container">
        <u-upload :previewImage="true" :max-count="maxCount" :auto-upload="false" @afterRead="handleAfterRead"
            @beforeRead="handleBeforeRead" :useBeforeRead="true" @delete="handleDelete" :multiple="multiple"
            :accept="accept" :fileList="fileList">
            <slot name="file"></slot>
        </u-upload>
    </view>
</template>

<script>
import { getToken, tempFilePathToFile, compressFile } from "@/utils";
import { uploadFile } from "@/utils/api";

export default {
    data() {
        return {
            fileList: [
                // {
                //     url: "/dev-api/profile/upload/2024/10/30/公司logo_20241030231031A009.jpg",
                // },
            ],
        };
    },
    props: {
        value: [String, Object, Array],
        maxCount: {
            type: Number,
            default: 1,
        },
        // 文件大小限制 M
        fileSize: {
            type: Number,
            default: 20,
        },
        fileType: {
            type: Array,
            default: () => ["jpg", "png", "jpeg"],
        },
        accept: {
            type: String,
            default: "image",
        },
        multiple: {
            type: Boolean,
            default: false,
        },
        //  是否压缩的阈值 单位M
        threshold: {
            type: Number,
            default: 1,
        },
    },
    watch: {
        value: {
            handler(val) {
                if (val) {
                    // 首先将值转为数组
                    const list = Array.isArray(val)
                        ? val
                        : this.value.split(",");
                    // 然后将数组转为对象数组
                    this.fileList = list.map((item) => {
                        if (typeof item === "string") {
                            let baseURL = this.globalVar.request.baseURL;
                            if (item.indexOf(baseURL) === -1) {
                                item = {
                                    name: baseURL + item,
                                    url: baseURL + item,
                                };
                            } else {
                                item = { name: item, url: item };
                            }
                        }
                        return item;
                    });
                } else {
                    this.fileList = [];
                    return [];
                }
            },
            deep: true,
            immediate: true,
        },
    },
    methods: {
        // uploadFilePromise(url) {
        //     return new Promise((resolve, reject) => {
        //         uni.uploadFile({
        //             url: `${this.globalVar.request.baseURL}/common/upload`, // 仅为示例,非真实的接口地址
        //             filePath: url,
        //             name: "file",
        //             header: {
        //                 Authorization: "Bearer " + getToken(),
        //             },
        //             success: (res) => {
        //                 setTimeout(() => {
        //                     resolve(JSON.parse(res.data));
        //                 }, 1000);
        //             },
        //         });
        //     });
        // },
        uploadFilePromise(file) {
            return new Promise(async (resolve, reject) => {
                try {
                    let formData = new FormData();
                    formData.append("file", file, file.name);
                    fetch(`${this.globalVar.request.baseURL}/common/upload`, {
                        method: "POST",
                        headers: {
                            Authorization: "Bearer " + getToken(),
                        },
                        body: formData,
                    })
                        .then((res) => res.json())
                        .then((res) => {
                            resolve(res);
                        });
                } catch (error) {
                    console.log(error);
                    resolve(null);
                }
            });
        },

        handleBeforeRead(e) {
            let files = typeof e.file === "object" ? [e.file] : e.file;
            let isLimitSize = files.some(
                (file) => file.size > this.fileSize * 1024 * 1024
            );
            if (isLimitSize) {
                this.$u.toast(`文件大小不能超过${this.fileSize}MB`);
                return false;
            }
            let isLimitType = files.some(
                (file) => !this.fileType.includes(file.name.split(".").pop())
            );
            if (isLimitType) {
                this.$u.toast(`仅支持${this.fileType.join(",")}格式`);
                return false;
            }
            return true;
        },

        async handleAfterRead(e) {
            let files = typeof e.file === "object" ? [e.file] : e.file;
            files.map((item) => {
                this.fileList.push({
                    ...item,
                    status: "uploading",
                    message: "上传中",
                });
            });
            let start = 0;
            for (let i = 0; i < files.length; i++) {
                let item = files[i];
                let file = await tempFilePathToFile({
                    tempFilePath: item.url,
                    fileName: item.name,
                });
                // 判断是否需要压缩
                if (file.size > this.threshold * 1024 * 1024) {
                    file = await compressFile(file, { quality: 0.2 });
                }
                let res = await this.uploadFilePromise(file);
                if (res.code == 200) {
                    this.fileList.splice(
                        start,
                        1,
                        Object.assign(item, {
                            status: "success",
                            message: "",
                            url: this.globalVar.request.baseURL + res.fileName,
                        })
                    );
                } else {
                    this.fileList.splice(
                        start,
                        1,
                        Object.assign(item, {
                            status: "error",
                            message: "上传失败",
                        })
                    );
                }
                start++;
            }
            this.$emit("input", this.listToString(this.fileList));
            let list = this.fileList.map((item) => {
                item.url = item.url.replace(this.globalVar.request.baseURL, "");
                return item;
            });
            this.$emit("change", list);
        },
        handleDelete(e) {
            this[`fileList`].splice(e.index, 1);
            this.$emit("input", this.listToString(this.fileList));
            // this.$emit("change", this.fileList);
        },
        // 对象转成指定字符串分隔
        listToString(list, separator) {
            let strs = "";
            separator = separator || ",";
            for (let i in list) {
                if (list[i].url) {
                    strs +=
                        list[i].url.replace(
                            this.globalVar.request.baseURL,
                            ""
                        ) + separator;
                }
            }
            return strs != "" ? strs.substr(0, strs.length - 1) : "";
        },
    },
};
</script>

<style scoped>
.upload-container {
    padding: 20px;
}

.upload-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px dashed #d9d9d9;
    border-radius: 5px;
    padding: 20px;
    cursor: pointer;
}

.upload-file {
    position: relative;
    margin-top: 10px;
}

.upload-file image {
    border: 1px solid #d9d9d9;
    border-radius: 5px;
}

.upload-file .u-icon {
    position: absolute;
    top: 5px;
    right: 5px;
}
</style>

5. utils/index.js 代码

import Compressor from 'compressorjs';

export const getToken = () => uni.getStorageSync('token') || '';

export const removeToken = () => uni.removeStorageSync('token');

export const base64Encode = (str) => {
    // 将字符串转换为 UTF-8 编码的字节数组
    const utf8Bytes = new TextEncoder().encode(str);
    // 将字节数组转换为 Base64 字符串
    return btoa(String.fromCharCode.apply(null, utf8Bytes));
};

export const base64Decode = (base64) => {
    // 将 Base64 字符串转换为字节数组
    const byteString = atob(base64);
    // 将字节数组转换为 UTF-8 字符串
    const bytes = new Uint8Array(byteString.length);
    for (let i = 0; i < byteString.length; i++) {
        bytes[i] = byteString.charCodeAt(i);
    }
    return new TextDecoder().decode(bytes);
};

export const goPageByPath = (path, query = {}) => {
    if (!path) {
        throw new Error('缺少必须的path参数');
    }
    let queryLen = Object.keys(query).length;
    let queryStr = '';
    if (queryLen) {
        queryStr = Object.keys(query)
            .map((key) => `${key}=${query[key]}`)
            .join('&');
        queryStr = `?${queryStr}`;
    }
    uni.navigateTo({url: `${path}${queryStr}`});
};

// 表单重置
export function resetForm(refName) {
    if (this.$refs[refName]) {
        this.$refs[refName].resetFields();
    }
}

// 判断 是否是数字
export const isStrictNumber = (value) => {
    return typeof value === 'number' && !isNaN(value);
};

// 获取文件的后缀名,不包含 .
export const getFileExt = (fileName) => {
    return fileName.split('.').pop();
};

// 临时路径转file对象 针对h5端
export const tempFilePathToFile = ({tempFilePath, fileName}) => {
    if (!tempFilePath) {
        return Promise.resolve(null);
    }
    return new Promise(async (resolve, reject) => {
        try {
            const response = await fetch(tempFilePath);
            const blob = await response.blob();
            const mimeType = response.headers.get('content-type');
            const file = new File([blob], fileName, {type: mimeType});
            resolve(file);
        } catch (error) {
            resolve(null);
        }
    });
};

// 压缩文件
export const compressFile = (file, options) => {
    return new Promise((resolve, reject) => {
        new Compressor(file, {
            ...options,
            success(result) {
                resolve(result);
            },
            error(err) {
                reject(err);
            },
        });
    });
};

3. 总结说明

可能有更好的处理方式, 如果大家有更好的处理方式,可以在评论区,贴出代码,让播主也学习一下,学无止境

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部