文章目录
- 问题说明
- 使用说明
- 1.使用场景
- 2. 文件需要压缩
- 3. 使用技术
- 4. 代码如下
- 5. utils/index.js 代码
- 3. 总结说明
问题说明
- 只针对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. 总结说明
可能有更好的处理方式, 如果大家有更好的处理方式,可以在评论区,贴出代码,让播主也学习一下,学无止境