大家好,我是喝西瓜汁的兔叽,今天给大家分享前端上传excel文件给后端,并且对excel的内容进行检验的需求。
明确需求
首先我们来看一下功能需求:
-
支持用户上传文件给服务端
-
且上传的格式必须为
.xlsx
格式 -
上传文件的内容必须符合以下规定:
- 第一行标题必须是'imei'和'sn'的顺序
- imei列所有的数据必须是数字类型
- excel中必须存在Sheet1
-
将文件上传服务端的结果(url)/错误信息通知给用户。
效果图
思路
- 我们使用
ant-design
的upload组件实现文件上传的功能。 - 读取excel文件,我们需要用到一个第三方库
xlsx
。 - 对于excel文件内容的合法检验,我们放置在upload组件的
:before-upload
的Api中进行,符合验证再上传,不符合提示用户错误信息。 - 而验证逻辑我们是通过js的
FileReader()
来读取文件,然后再使用xlsx库
读取excel文件。读取excel文件主要用到的api是:- XLSX.read() //读取excel文件
- XLSX.utils.sheet_to_json()//将文件内容转换为json格式
实现
** 安装xlsx库 **
sh
npm i xlsx -S
核心思路就是在上传文件前,在before-upload
中使用xlsx库对excel文件进行内容的读取。
对upload组件中的一些属性解释:
js
fileList:上传的文件列表
name:传递给服务端的文件名称(服务端要求的,具体的名字需要根据各自公司内部业务来指定)
action:文件上传的地址
headers:给服务端传递的请求头,这里主要传递token
accept:指定上传的类型
before-upload:上传之前进行的操作
@change:文件切换触发该事件
vue
<template>
<a-upload-dragger
v-model:fileList="fileList"
name="files"
:action="uploadUrl"
:headers="headers"
accept=".xlsx"
:maxCount="1"
:before-upload="beforeUpload"
@change="onUploadChange"
>
<p class="ant-upload-drag-icon">
<inbox-outlined />
</p>
<p class="ant-upload-text">
请点击上传或者拖拽到此(只支持.xlsx格式文件)
</p>
</a-upload-dragger>
</template>
<script setup lang="ts">
import * as XLSX from 'xlsx';
import { useAccessStore } from '@vben/stores';
//上传文件列表
const fileList = ref([]);
//上传结果消息
let uploadMsg = '';
//上传的服务端地址
const uploadUrl = `${BASEURL}/device/status/batch_update_by_excel`;
//给header加上token
const accessStore = useAccessStore();
const headers = {
Authorization: `Bearer ` + accessStore.accessToken,
};
//上传文件改变时触发的事件
const onUploadChange = (info: UploadChangeParam) => {
console.log('info', info);
const status = info.file.status;
if (status === 'done') {
//uploadMsg是服务端返回的消息,具体的需要根据自己公司的实际业务来指定
uploadMsg = info.file.response.data.url
? info.file.response.data.url
: info.file.response.msg;
} else if (status === 'error') {
uploadMsg = info.file.response.msg;
}
};
//!!!!!!!!!!上传前校验,主要核心逻辑在这里!!!!!!!!!
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
console.log(file);
//验证是否是.xlsx格式
const isExcel =
file.type =='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
if (!isExcel) {
message.error(`${file.name}不是.xlsx格式`);
}
// 验证excel内容
console.log(file);
const reader = new FileReader();//创建FileReader对象,用来读取文件
//当文件读取加载完毕,使用xlsx来读取excel文件内容
reader.onload = (e) => {
console.log('filereader', e.target);
const data = e.target?.result;//获取到文件内容
const workbook = XLSX.read(data, { type: 'binary' });//读取excel内容
console.log('workbook', workbook);
// 验证文件名
if (!workbook.SheetNames.includes('Sheet1')) {
fileList.value = [];
return message.error(`文件内必须存在sheet1`);
}
//验证文件内容
const worksheet = workbook.Sheets['Sheet1'];//拿到sheet1的内容
//将sheet1的内容转为json数据
const jsonData = XLSX.utils.sheet_to_json<any>(worksheet as any, {
header: 1,
});
console.log('jsonData', jsonData);
// 验证题头
const headers = jsonData[0]!;
if (headers.length < 2 || headers[0] !== 'imei' || headers[1] !== 'sn') {
fileList.value = [];
return message.error('文件内容题头必须是 imei 和 sn 的顺序');
}
// 验证内容imei列为纯数字
for (let i = 1; i < jsonData.length; i++) {
console.log(jsonData[i][0]);
if (jsonData[i][0] !== '') {
if (!/^\d+$/.test(jsonData[i][0])) {
fileList.value = [];
return message.error(`第${i + 1}行imei列必须为纯数字`);
}
}
}
};
reader.readAsArrayBuffer(file);//使用arrayBuffer格式读取文件
//符合规则上传文件,不符合就不上传
return isExcel || Upload.LIST_IGNORE;
};
</script>
完整代码
vue
<template>
<!-- 入库按钮 -->
<a-button type="primary" @click="uploadModal = true">
<PlusOutlined />
批量入库
</a-button>
<!-- upload modal -->
<a-modal
v-model:open="uploadModal"
title="设备入库"
:width="600"
@ok="uploadModal = false"
:afterClose="onClearUpload"
>
<div class="p-4">
<a-upload-dragger
v-model:fileList="fileList"
name="files"
:action="uploadUrl"
:headers="headers"
accept=".xlsx"
:maxCount="1"
:before-upload="beforeUpload"
@change="onUploadChange"
>
<p class="ant-upload-drag-icon">
<inbox-outlined />
</p>
<p class="ant-upload-text">
请点击上传或者拖拽到此(只支持.xlsx格式文件)
</p>
</a-upload-dragger>
<!-- message box -->
<p style="margin: 20px 0 0">上传结果:</p>
<div class="uploadmsg">
<span v-if="isUrl(uploadMsg)">
<a class="text-blue-500 underline" :href="uploadMsg">{{
uploadMsg
}}</a>
</span>
<span v-else class="text-red-500">{{ uploadMsg }}</span>
</div>
</div>
</a-modal>
</template>
<script setup lang="ts">
import {ref } from 'vue';
import {
message,
Modal,
Upload,
type UploadChangeParam,
type UploadProps,
} from 'ant-design-vue';
import { InboxOutlined, PlusOutlined } from '@ant-design/icons-vue';
import * as XLSX from 'xlsx';
import { useAccessStore } from 'stores';
// upload excel
const uploadModal = ref<boolean>(false);
const fileList = ref([]);
let uploadMsg = '';
const BASEURL = import.meta.env.VITE_GLOB_API_URL;
const uploadUrl = `${BASEURL}/device/status/batch_update_by_excel`;
console.log('uploadUrl', uploadUrl);
//关闭清空modal中的内容
const onClearUpload = () => {
fileList.value = [];
uploadMsg = '';
};
//获取token
const accessStore = useAccessStore();
const headers = {
Authorization: `Bearer ` + accessStore.accessToken,
};
//上传前验证
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
console.log(file);
//验证excel
const isExcel =
file.type ==
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
if (!isExcel) {
message.error(`${file.name}不是.xlsx格式`);
}
// 验证excel内容
console.log(file);
const reader = new FileReader();
reader.onload = (e) => {
console.log('filereader', e.target);
const data = e.target?.result;
const workbook = XLSX.read(data, { type: 'binary' });
console.log('workbook', workbook);
// 验证文件名
if (!workbook.SheetNames.includes('Sheet1')) {
fileList.value = [];
return message.error(`文件内必须存在sheet1`);
}
//验证文件内容
const worksheet = workbook.Sheets['Sheet1'];
const jsonData = XLSX.utils.sheet_to_json<any>(worksheet as any, {
header: 1,
});
console.log('jsonData', jsonData);
// 验证题头
const headers = jsonData[0]!;
if (headers.length < 2 || headers[0] !== 'imei' || headers[1] !== 'sn') {
fileList.value = [];
return message.error('文件内容题头必须是 imei 和 sn 的顺序');
}
// 验证内容imei列为纯数字
for (let i = 1; i < jsonData.length; i++) {
console.log(jsonData[i][0]);
if (jsonData[i][0] !== '') {
if (!/^\d+$/.test(jsonData[i][0])) {
fileList.value = [];
return message.error(`第${i + 1}行imei列必须为纯数字`);
}
}
}
};
reader.readAsArrayBuffer(file);
return isExcel || Upload.LIST_IGNORE;
};
/*判断是否是url*/
function isUrl(value: string) {
try {
new URL(value);
return true;
} catch {
return false;
}
}
//上传改变事件
const onUploadChange = (info: UploadChangeParam) => {
console.log('info', info);
const status = info.file.status;
if (status === 'done') {
uploadMsg = info.file.response.data.url
? info.file.response.data.url
: info.file.response.msg;
} else if (status === 'error') {
uploadMsg = info.file.response.msg;
}
};
</script>
以上就是本期分享的内容,更多的api请去查看xlsx库的官网哦~
朋友,我是喝西瓜汁的兔叽,感谢您的阅读,衷心祝福您和家人身体健康,事事顺心。