第三方库XLSX: 前端上传excel文件,并对excel文件内容做校验。

大家好,我是喝西瓜汁的兔叽,今天给大家分享前端上传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库的官网哦~


朋友,我是喝西瓜汁的兔叽,感谢您的阅读,衷心祝福您和家人身体健康,事事顺心。

相关推荐
乐多_L33 分钟前
使用vue3框架vue-next-admin导出表格excel(带图片)
前端·javascript·vue.js
南望无一1 小时前
React Native 0.70.x如何从本地安卓源码(ReactAndroid)构建
前端·react native
Mike_188702783511 小时前
1688代采下单API接口使用指南:实现商品采集与自动化下单
前端·python·自动化
鲨鱼辣椒️面1 小时前
HTML视口动画
前端·html
一小路一1 小时前
Go Web 开发基础:从入门到实战
服务器·前端·后端·面试·golang
堇舟1 小时前
HTML第一节
前端·html
纯粹要努力1 小时前
前端跨域问题及解决方案
前端·javascript·面试
小刘不知道叫啥1 小时前
React源码揭秘 | 启动入口
前端·react.js·前端框架
kidding7231 小时前
uniapp引入uview组件库(可以引用多个组件)
前端·前端框架·uni-app·uview
合法的咸鱼1 小时前
uniapp 使用unplugin-auto-import 后, vue文件报红问题
前端·vue.js·uni-app