JS-Vue获取图片信息
今天产品的一个需求是要求用户上传图片时候,对格式进行校验
Demo预览 http://demo.webwlx.cn/#/ImgInfoDemo
具体需求
- 获取图片信息(格式,宽高,大小)
- 文件唯一性校验(MD5值)
实现思路
- 上传文件,使用Element Ui 的上传组件 监听Change
- 获取file文件的临时路径
- 使用临时路径,获取宽高
- 获取MD5,文件的唯一性校验
代码片段
// cnpm i SparkMD5 -S
import SparkMD5 from "spark-md5";
/* 获取文件大小 */
const getSize = (size) => {
let data = "";
if (size < 0.1 * 1024) {
//如果小于0.1KB转化成B
data = size.toFixed(2) + "B";
} else if (size < 0.1 * 1024 * 1024) {
//如果小于0.1MB转化成KB
data = (size / 1024).toFixed(2) + "KB";
} else if (size < 0.1 * 1024 * 1024 * 1024) {
//如果小于0.1GB转化成MB
data = (size / (1024 * 1024)).toFixed(2) + "MB";
} else {
//其他转化成GB
data = (size / (1024 * 1024 * 1024)).toFixed(2) + "GB";
}
let sizestr = data + "";
let len = sizestr.indexOf(".");
let dec = sizestr.substr(len + 1, 2);
if (dec == "00") {
//当小数点后为00时 去掉小数部分
return sizestr.substring(0, len) + sizestr.substr(len + 3, 2);
}
return sizestr;
};
/* 获取Md5 */
const getMd5 = (file) => {
return new Promise((reslove) => {
let fileReader = new FileReader();
let spark = new SparkMD5.ArrayBuffer();
// 获取文件二进制数据
fileReader.readAsArrayBuffer(file);
fileReader.onload = function (e) {
spark.append(e.target.result);
let md5 = spark.end();
reslove(md5);
};
});
};
/* 获取图片宽高 */
const getImgInfo = (fileUrl) => {
return new Promise((reslove) => {
let img = new Image();
img.src = fileUrl;
img.style.display = "none";
img.onload = () => {
let { width, height } = img;
reslove({ width, height });
};
});
};
/* 获取完成的文件信息 */
const fileInfo = async (file) => {
let fileInfo = new Object();
let { raw, size } = file;
let { type: fileType } = raw;
let fileUrl = window.URL.createObjectURL(raw);
fileInfo.fileUrl = fileUrl; // 临时路径
fileInfo.fileType = fileType;
if (fileType.indexOf("image") !== -1) {
/* 类型 图片 */
fileInfo.type = 1;
let { width, height } = await getImgInfo(fileUrl);
Object.assign(fileInfo, { width, height });
}
fileInfo.size = size;
fileInfo.sizeInfo = getSize(size);
let md5 = await getMd5(raw);
fileInfo.md5 = md5;
return fileInfo;
};
export { fileInfo };
使用示例
<template>
<div class="page-container">
<div class="wid">
<h1>读取图片信息</h1>
<div class="upload-box">
<div class="flex">
<el-upload class="upload-demo" drag multiple :accept="uploadConfig.accept" :auto-upload="uploadConfig.autoUpload" :action="uploadConfig.action" :show-file-list="uploadConfig.showFileList" :on-change="fileChange">
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
<div class="config-box">
<p>
图片大小:<span>{{ fileConfig.size }} MB</span>
</p>
<p>
图片宽度:<span>{{ fileConfig.width }} px</span>
</p>
<p>
图片宽度:<span>{{ fileConfig.height }} px</span>
</p>
</div>
</div>
<showFileVue v-model="fileList" />
</div>
</div>
</div>
</template>
<script>
import { fileInfo } from "./getImgInfo";
import showFileVue from "./components/showFile.vue";
export default {
name: "ImgInfoDemo",
components: { showFileVue },
data() {
return {
fileList: [],
uploadConfig: {
action: "", // 上传Url,
showFileList: false,
autoUpload: false,
accept: "image/jpeg,image/jpg,image/gif,image/png",
},
fileConfig: {
size: 10, // Mb
width: 1920,
height: 1080,
},
};
},
created() {},
methods: {
/* 选择文件回调 */
async fileChange(file) {
let newFile = await fileInfo(file);
/* 判断是否上传过了 */
let md5Arr = this.fileList.map((item) => item.md5);
if (md5Arr.indexOf(newFile.md5) !== -1) {
this.$message({ message: `${file.name}已经选择过了,请不要重复选择`, type: "success" });
return false;
}
Object.assign(file, newFile);
let { status, errArr } = await this.checkFile(file);
Object.assign(file, { status, errArr });
this.fileList.push(file);
},
/* 校验是否符合要求 */
async checkFile(file) {
let status = false; // 校验状态
let errArr = [];
let { width, height, size } = this.fileConfig;
if (width !== file.width) {
errArr.push("文件宽度错误");
}
if (height !== file.height) {
errArr.push("文件高度错误");
}
if (file.size > size * 1024 * 1024) {
errArr.push("文件大小超出");
}
status = !errArr.length;
return { status, errArr };
},
},
};
</script>
<style lang="scss" scoped>
.flex {
display: flex;
align-items: flex-start;
.config-box {
margin-left: 40px;
p {
line-height: 2;
span {
color: red;
}
}
}
}
</style>
封装显示组件,传入FIleList
<template>
<div v-if="fileList.length" class="file-container">
<el-card :body-style="{ padding: '0px 10px 10px 10px' }">
<div>
<el-table :data="fileList" style="width: 100%">
<el-table-column type="index" label="序号" width="100" />
<el-table-column prop="prop" label="素材文件" width="240" align="center">
<template #default="{ row }">
<div class="metrial-box">
<div class="img-box">
<img :src="row.fileUrl" :alt="row.name" />
</div>
<p class="title text-center">{{ row.name }}</p>
</div>
</template>
</el-table-column>
<el-table-column prop="prop" label="素材信息">
<template #default="{ row }">
<div class="file-info-box">
<p>分辨率:{{ row.width + "*" + row.height }}</p>
<p>格式:{{ row.fileType }}</p>
<p>大小:{{ row.sizeInfo }}</p>
</div>
</template>
</el-table-column>
<el-table-column prop="prop" label="格式校验">
<template #default="{ row }">
<div class="flex-alc status">
<img v-if="row.status" src="https://willmid-img.oss-cn-shanghai.aliyuncs.com/2021/11/25/c9f93ac24a509e5a0b8a5715b23249b3.png" alt="" />
<img v-else src="https://willmid-img.oss-cn-shanghai.aliyuncs.com/2021/11/25/c92383ea2fa7ccaf1917dafb77a4556a.png" alt="" />
{{ row.status ? "校验通过" : "校验失败" }}
</div>
<p v-if="!row.status" class="tips">{{ row.errArr.join("-") }}</p>
</template>
</el-table-column>
<el-table-column type="index" label="操作" width="100">
<template #default="scope">
<el-button @click="delBtn(scope.$index)" type="danger" size="mini" icon="el-icon-close">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-card>
</div>
</template>
<script>
export default {
name: "ShowFileList",
props: {
value: {
type: Array,
default: new Array(),
},
},
data() {
return {};
},
computed: {
fileList() {
return this.value;
},
},
methods: {
/* 删除素材 */
delBtn(index) {
this.fileList.splice(index, 1);
},
},
};
</script>
<style lang="scss" scoped>
.progress {
padding: 20px 0;
}
:deep(.el-table th) {
background: none !important;
margin-bottom: 10px;
}
.tips {
color: red;
}
.status {
display: flex;
align-items: center;
img {
margin-right: 10px;
width: 20px;
height: 20px;
display: block;
}
.mds-chenggong {
color: red;
}
.mds-shibai {
color: green;
}
}
.file-container {
margin-top: 20px;
.metrial-box {
display: flex;
justify-content: center;
flex-direction: column;
.title {
width: 100%;
margin-top: 10px;
}
.img-box {
height: 110px;
border-radius: 4px;
overflow: hidden;
img {
display: block;
width: 100%;
height: 100%;
object-fit: contain;
}
}
}
}
</style>