【前后前】导入Excel文件闭环模型:Vue3前端上传Excel文件,【Java后端接收、解析、返回数据】,Vue3前端接收展示数据

【前后前】导入Excel文件闭环模型:Vue3前端上传Excel文件,【Java后端接收、解析、返回数据】,Vue3前端接收展示数据

一、Vue3前端上传(导入)Excel文件

ReagentInDialog.vue

<script setup lang="ts" name="ReagentInDialog">// 导入
const onImportClick = () => {// 模拟点击元素if (fileInputRef.value) {// 重置以允许重复选择相同文件fileInputRef.value.value = "";fileInputRef.value.click();}
};// 点击【导入】触发
const handleImport= async (e: Event) => {let dataList = [];try {tableLoading.value = true;// 获取文件对象const input = e.target as HTMLInputElement;if (!input.files?.length) return;const file = input.files[0];// 键值列名映射表const keyColMap: Record<string, string> = {试剂编号: "reagentNo",试剂名称: "reagentName",规格型号: "reagentSpec",单位: "reagentUnit",批号: "batchNo",有效期至: "validityDate",入库数量: "amount",入库金额: "total"};// 导入文件,由前端解析文件,获取数据// dataList = await importExcelFileByClient(file, keyColMap);// 导入文件,由后端解析文件,获取数据dataList = await importExcelFileByServer(file, keyColMap);} finally {tableLoading.value = false;}
}</script><template><el-button type="primary" plain @click="onImportClick">导入</el-button><!-- 文件输入元素,不显示,通过点击按钮【导入】执行 onImportClick,模拟点击该元素,从而触发 handleImport事件 --><inputref="fileInputRef"type="file"accept=".xls, .xlsx"style="display: none"@change="handleImport" /></template>

excelUtils.ts

import { formatJson } from "@/utils/formatter";
import { convertFileSize } from "@/utils/pubUtils";
import { ElMessage } from "element-plus";
import * as xlsx from "xlsx";
import { uploadFileService } from "@/api/upload";/*** 从Excel文件导入数据,由后端解析文件,获取数据* @param file 导入文件* @param colKeyMap 列名键值映射,key --> value,如:excel中列名为【样品编号】,其键值设置对应为【sampleNo】* @returns 列表数据*/
export async function importExcelFileByServer(file: any, keyColMap?: Record<string, string>) {// 定义及初始化需要返回的列表数据let dataList: any[] = [];// 文件校验// 校验文件名后缀if (!/\.(xls|xlsx)$/.test(file.name)) {ElMessage.warning("请导入excel文件!");return dataList;}// 校验文件格式// application/vnd.ms-excel 为 .xls文件// application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 为 .xlsx文件else if (file.type !== "application/vnd.ms-excel" &&file.type !== "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") {ElMessage.warning("excel文件已损坏,请检查!");return dataList;}// 校验文件大小else if (convertFileSize(file.size, "B", "MB") > 1) {ElMessage.warning("文件大小不能超过1MB!");return dataList;}// 文件读取const fileReader = new FileReader();// 以二进制的方式读取文件内容fileReader.readAsArrayBuffer(file);// 等待打开加载完成文件,其实就是执行 fileReader.onloadend = () => {},返回 true 表示成功,false 表示失败const result = await loadedFile(fileReader);if (result) {// 通过 FormData 对象实现文件上传const formData = new FormData();// 将文件对象 file 添加到 formData 对象中,uploadFile 需要与后端接口中接收文件的参数名一致formData.append("uploadFile", file);// 发送请求,上传文件到后端服务器,后端接收文件,进行解析,并返回数据集const result = await uploadFileService(formData);dataList = keyColMap ? formatJson(result.data, keyColMap) : result.data;}// 返回列表数据return dataList;
}

upload.ts

import request from "@/utils/request";/*** 上传文件,后端解析Excel文件,返回解析后的列表数据* @param file 文件,表单数据* @returns 列表数据*/
export const uploadFileService = (file: FormData) => {return request.post("/upload/parseExcelFile", file, {// 上传文件,需设置 headers 信息,将"Content-Type"设置为"multipart/form-data"headers: {"Content-Type": "multipart/form-data"}});
};

二、Java后端接收、解析、返回数据

UploadController.java

package com.weiyu.controller;import com.weiyu.utils.ExcelUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import java.util.List;
import java.util.Map;/*** 上传 Controller*/
@RestController
@RequestMapping("/upload")
@Slf4j
public class UploadController {// 上传文件,后端解析Excel文件,返回解析后的列表数据// 因为前端是用 "Content-Type": "multipart/form-data" 的方式发送的请求,这里就不能用 @RequestBody,而是用 MultipartFile// 并且形参名称 uploadFile 需要与前端定义的保持一致@PostMapping("/parseExcelFile")public ResponseEntity<?> uploadAndParseExcelFile(MultipartFile uploadFile) {log.info("【上传文件】,解析Excel文件,/upload/parseExcelFile,uploadFile = {}", uploadFile);try {// 验证文件if (uploadFile.isEmpty()) {return ResponseEntity.badRequest().body("文件为空");}// 验证文件类型String contentType = uploadFile.getContentType();if (contentType == null || (!contentType.equals("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") && !contentType.equals("application/vnd.ms-excel"))) {return ResponseEntity.badRequest().body("仅支持 Excel 文件 (.xlsx, .xls)");}// 解析 ExcelList<Map<String, Object>> data = ExcelUtils.parseExcel(uploadFile);// 返回解析结果return ResponseEntity.ok(data);} catch (Exception e) {return ResponseEntity.internalServerError().body("解析失败: " + e.getMessage());}}
}

三、Vue3前端接收展示数据

1、正常发送请求数据

2、正常接收响应数据

3、解析出错

四、后端修改方案

UploadController.java

package com.weiyu.controller;import com.weiyu.pojo.Result;
import com.weiyu.utils.ExcelUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;import java.util.List;
import java.util.Map;/*** 上传 Controller*/
@RestController
@RequestMapping("/upload")
@Slf4j
public class UploadController {// 上传文件,后端解析Excel文件,返回解析后的列表数据// 因为前端是用 "Content-Type": "multipart/form-data" 的方式发送的请求,这里就不能用 @RequestBody,而是用 MultipartFile// 并且形参名称 uploadFile 需要与前端定义的保持一致@PostMapping("/parseExcelFile")@ResponseBody // 直接序列化返回值,使用 Result<List<Map<String, Object>>> 替换 ResponseEntity<?>public Result<List<Map<String, Object>>> uploadAndParseExcelFile(MultipartFile uploadFile) {log.info("【上传文件】,解析Excel文件,/upload/parseExcelFile,uploadFile = {}", uploadFile);try {// 验证文件
//            if (uploadFile.isEmpty()) {
//                return ResponseEntity.badRequest().body("文件为空");
//            }// 验证文件类型
//            String contentType = uploadFile.getContentType();
//            if (contentType == null || (!contentType.equals("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") && !contentType.equals("application/vnd.ms-excel"))) {
//                return ResponseEntity.badRequest().body("仅支持 Excel 文件 (.xlsx, .xls)");
//            }// 解析 ExcelList<Map<String, Object>> data = ExcelUtils.parseExcel(uploadFile);// 返回解析结果
//            return ResponseEntity.ok(data);return Result.success(data);} catch (Exception e) {
//            return ResponseEntity.internalServerError().body("解析失败: " + e.getMessage());throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "解析失败: " + e.getMessage(), e);}}
}

前端导入效果

五、后端完善方案 

UploadController.java

package com.weiyu.controller;import com.weiyu.pojo.Result;
import com.weiyu.service.UploadService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import java.util.List;
import java.util.Map;/*** 上传 Controller*/
@RestController
@RequestMapping("/upload")
@Slf4j
public class UploadController {@Autowiredprivate UploadService uploadService;// 上传文件,后端解析Excel文件,返回解析后的列表数据// 因为前端是用 "Content-Type": "multipart/form-data" 的方式发送的请求,这里就不能用 @RequestBody,而是用 MultipartFile// 并且形参名称 uploadFile 需要与前端定义的保持一致@PostMapping("/parseExcelFile")// @ResponseBody // 直接序列化返回值,使用 Result<List<Map<String, Object>>> 替换 ResponseEntity<?>public Result<List<Map<String, Object>>> uploadAndParseExcelFile(MultipartFile uploadFile) {log.info("【上传文件】,解析Excel文件,/upload/parseExcelFile,uploadFile = {}", uploadFile);List<Map<String, Object>> data = uploadService.parseExcelFile(uploadFile);return Result.success(data);}
}

UploadServiceImpl.java

package com.weiyu.service.impl;import com.weiyu.service.UploadService;
import com.weiyu.utils.ExcelUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.util.List;
import java.util.Map;/*** 上传 Service 接口实现*/
@Service
public class UploadServiceImpl implements UploadService {// 解析 Excel 文件@Overridepublic List<Map<String, Object>> parseExcelFile(MultipartFile uploadFile) {try {// 解析 Excelreturn ExcelUtils.parseExcel(uploadFile);} catch (IOException e) {throw new RuntimeException(e);}}
}

excel文件处理工具类 ExcelUtils.java 

package com.weiyu.utils;import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.NumberToTextConverter;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.ZoneId;
import java.util.*;/*** excel文件处理工具类*/
@Slf4j
@Component //通过@Component注解,将该工具类交给ICO容器管理,需要使用的时候不需要new,直接@Autowired注入即可
public class ExcelUtils {public static List<Map<String, Object>> parseExcel(MultipartFile file) throws IOException {try (InputStream inputStream = file.getInputStream()) {Workbook workbook = WorkbookFactory.create(inputStream);Sheet sheet = workbook.getSheetAt(0);// 获取表头行Row headerRow = sheet.getRow(0);if (headerRow == null) {return Collections.emptyList();}// 处理表头(处理重复列名)List<String> headers = processHeaders(headerRow);// 解析数据行List<Map<String, Object>> data = new ArrayList<>();for (int i = 1; i <= sheet.getLastRowNum(); i++) {Row row = sheet.getRow(i);if (row == null) continue;Map<String, Object> rowData = parseRow(row, headers);if (!rowData.isEmpty()) {data.add(rowData);}}return data;}}private static List<String> processHeaders(Row headerRow) {List<String> headers = new ArrayList<>();Map<String, Integer> headerCount = new HashMap<>();for (Cell cell : headerRow) {String header = getCellValueAsString(cell).trim();// 处理空表头if (header.isEmpty()) {header = "Column_" + (cell.getColumnIndex() + 1);}// 处理重复表头int count = headerCount.getOrDefault(header, 0) + 1;headerCount.put(header, count);if (count > 1) {header = header + "_" + count;}headers.add(header);}return headers;}private static Map<String, Object> parseRow(Row row, List<String> headers) {Map<String, Object> rowData = new LinkedHashMap<>();DataFormatter formatter = new DataFormatter();for (int i = 0; i < headers.size(); i++) {String header = headers.get(i);Cell cell = row.getCell(i, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);// 根据单元格类型处理数据switch (cell.getCellType()) {case STRING:rowData.put(header, cell.getStringCellValue().trim());break;case NUMERIC:if (DateUtil.isCellDateFormatted(cell)) {// 日期类型处理rowData.put(header, cell.getDateCellValue().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());} else {// 数值类型处理double value = cell.getNumericCellValue();if (value == (int) value) {rowData.put(header, (int) value);} else {rowData.put(header, value);}}break;case BOOLEAN:rowData.put(header, cell.getBooleanCellValue());break;case FORMULA:// 公式单元格处理rowData.put(header, evaluateFormulaCell(cell));break;default:rowData.put(header, formatter.formatCellValue(cell));}}return rowData;}private static Object evaluateFormulaCell(Cell cell) {try {switch (cell.getCachedFormulaResultType()) {case NUMERIC:return cell.getNumericCellValue();case STRING:return cell.getStringCellValue();case BOOLEAN:return cell.getBooleanCellValue();default:return "";}} catch (Exception e) {return "FORMULA_ERROR";}}private static String getCellValueAsString(Cell cell) {if (cell == null) return "";switch (cell.getCellType()) {case STRING:return cell.getStringCellValue();case NUMERIC:return NumberToTextConverter.toText(cell.getNumericCellValue());case BOOLEAN:return String.valueOf(cell.getBooleanCellValue());case FORMULA:return evaluateFormulaCell(cell).toString();default:return "";}}
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/84815.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/84815.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络基础入门:从OSI模型到TCP/IP协议详解

网络基础入门&#xff1a;从OSI模型到TCP/IP协议详解 一、网络基础概念与OSI七层模型 1.1 网络通信的本质 计算机网络的核心是将抽象语言转换为二进制数据进行传输与计算&#xff0c;这一过程涉及多层抽象与转换&#xff1a; 应用层&#xff1a;人机交互—抽象语言------编…

Linux致命漏洞CVE-2025-6018和CVE-2025-6019

Qualys 最近披露了两个影响主流 Linux 发行版的本地权限提升 (LPE) 漏洞&#xff0c;分别是 CVE-2025-6018 和 CVE-2025-6019。这两个漏洞可以被串联利用&#xff0c;使得非特权用户在几秒钟内获得系统的 root 权限&#xff0c;从而实现对系统的完全控制。 一、漏洞详情 这两…

【Docker基础】Docker镜像管理:docker push详解

目录 引言 1 Docker镜像推送基础概念 1.1 什么是Docker镜像推送 1.2 镜像仓库概述 1.3 镜像标签与版本控制 2 docker push命令详解 2.1 基本语法 2.2 常用参数选项 2.3 实际命令示例 2.4 推送流程 2.5 步骤描述 3 镜像推送实践示例 3.1 登录管理 3.2 标签管理 3…

FPGA基础 -- Verilog行为建模之循环语句

行为级建模&#xff08;Behavioral Modeling&#xff09;是 Verilog HDL 中最接近软件编程语言的一种描述方式&#xff0c;适用于功能建模和仿真建模的初期阶段。在行为级中&#xff0c;循环语句&#xff08;loop statements&#xff09;是常见且重要的控制结构&#xff0c;用于…

从C学C++(7)——static成员

从C学C(7)——static成员 若无特殊说明&#xff0c;本博客所执行的C标准均为C11. static成员和成员函数 对于特定类型的全体对象而言&#xff0c;有时候可能需要访问一个全局的变量。比如说统计某种类型对象已创建的数量。 通常在C中使用全局变量来实现&#xff0c;如果我们…

大模型和ollama一起打包到一个docker镜像中

如何将大模型镜像和 Ollama 镜像打包在一个 Docker 镜像中 最近工作中有个需求是将ollama和大模型一起打成一个镜像部署&#xff0c;将自己的操作步骤分享给大家。将大模型与 Ollama 服务打包在同一个 Docker 镜像中&#xff0c;可以简化部署流程并确保环境一致性。下面详细介…

2025年渗透测试面试题总结-攻防研究员(应用安全)(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 攻防研究员(应用安全) 一、基础部分 1. HTTP状态码对比 2. HTTP请求方法核心作用 3. 网络分层协议速查表…

SpringBoot新闻项目学习day3--后台权限的增删改查以及权限管理分配

新增管理员修改管理员删除管理员登录 新增管理员 1.点击新增按钮打开一个对话框 2.确定新增对话框要显示哪些内容 3.提交 4.后端处理、保存 5.响应前端 vue代码 <template><!-- 新增代码内容是比较多的,建议抽取出来,定义到一个独立的vue文件中在列表组件中导入…

算法导论第二十五章 深度学习的伦理与社会影响

第二十五章 深度学习的伦理与社会影响 技术的光芒不应掩盖伦理的阴影 随着深度学习技术在各领域的广泛应用&#xff0c;其引发的伦理和社会问题日益凸显。本章将深入探讨这些挑战&#xff0c;并提供技术解决方案和最佳实践&#xff0c;引导读者构建负责任的人工智能系统。 25.…

Linux中ansible模块补充和playbook讲解

一、模块使用 1.1 Yum模块 功能&#xff1a;管理软件包&#xff0c;只支持RHEL&#xff0c;CentOS&#xff0c;fedora&#xff0c;不支持Ubuntu其它版本 参数说明name要操作的软件包名称&#xff0c;支持通配符&#xff08;如 httpd, nginx*&#xff09;&#xff0c;也可以是…

唐代大模型:智能重构下的盛世文明图谱

引言&#xff1a;当长安城遇见深度学习 一件唐代鎏金舞马衔杯银壶的虚拟复原品正通过全息投影技术演绎盛唐乐舞。这个跨越时空的场景&#xff0c;恰似唐代大模型技术的隐喻——以人工智能为纽带&#xff0c;连接起长安城的盛世气象与数字时代的文明重构。作为人工智能与历史学…

国产ARM/RISCV与OpenHarmony物联网项目(三)网关设备控制

一、设备控制界面与功能设计 程序界面运行与设计效果如下: 设备控制相关程序调用关系图如下&#xff1a; 其中device_control.html程序为网页界面显示程序&#xff0c;led_alarm.cgi程序为光线数据的报警超限数据设置与管理&#xff0c;led_control.cgi程序功能为对Led灯的开…

微信小程序反编译实战教程

在实际渗透测试或安全分析中&#xff0c;经常会遇到微信小程序中的签名加密&#xff08;sign&#xff09;机制&#xff0c;这些机制大多具备防重放、防篡改的特性&#xff0c;导致我们在抓包时难以直接复现请求。 &#x1f50d; 另一方面&#xff0c;一些小程序的代码中往往会…

【NLP入门系列三】NLP文本嵌入(以Embedding和EmbeddingBag为例)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 博主简介&#xff1a;努力学习的22级本科生一枚 &#x1f31f;​&#xff1b;探索AI算法&#xff0c;C&#xff0c;go语言的世界&#xff1b;在迷茫中寻找光芒…

文心一言(ERNIE Bot):百度打造的知识增强大语言模型

1. 产品概述 文心一言&#xff08;ERNIE Bot&#xff09;是百度自主研发的知识增强大语言模型&#xff0c;于2023年3月16日正式发布&#xff0c;对标OpenAI的ChatGPT&#xff0c;具备文本生成、多模态交互、逻辑推理、中文理解等能力。该模型基于百度的飞桨深度学习平台和文心…

Java-49 深入浅出 Tomcat 手写 Tomcat 实现【02】HttpServlet Request RequestProcessor

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月13日更新到&#xff1a; AI炼丹日志-28 - Aud…

在VB.net中,文本插入的几个自定义函数

一、如果你是高手&#xff0c;一定“识货”&#xff0c;分享给你 二、可应用于文本插入的几种方式&#xff1a;6种 三、需要用到以下的几个函数&#xff1a; 上代码&#xff1a; Module TextModule <summary> 在指定位置插入文本 </summary> <p…

QC -io 服务器排查报错方式/报错: Failed to convert string to integer of varId variable!“

进断点控制台有报错之后&#xff0c;复制报错信息到 头部菜单栏 1.编辑 -> 2.Find/Replace ->3.Advanced Find ->4. Project“xxxxx” 能找到问题点 再分析定位 在排查报错时候&#xff0c;进入了这个报错&#xff0c;msgInfo "MyTcpRedis: Failed to conver…

c++中auto与decltype使用

在 C11及后续版本中&#xff0c;关键字auto和decltype都是用于类型推导的&#xff0c;但它们的使用场景和行为有所不同。 1. auto 关键字 作用 auto 用于自动推导变量的类型&#xff0c;由编译器根据初始化表达式来确定。 常见用法 // 基本用法 auto x 42; // int…

LabVIEW机器视觉零件检测

基于LabVIEW 图形化编程平台与机器视觉技术&#xff0c;构建集图像采集、处理、尺寸计算与合格性分析于一体的自动化检测方案。通过模块化硬件架构与自适应算法设计&#xff0c;实现对机械零件多维度尺寸的非接触式高精度测量&#xff0c;相比人工检测效率提升 12 倍&#xff0…