Apache POI导入导出excel文件实战

news2025/1/17 3:51:36

文章目录

    • 前言
    • 技术栈
    • 1、引入依赖
    • 2、导入代码实现
    • 3、导出代码实现
      • 3.1、准备导出文件模板
      • 3.2、导出代码实现
    • 4、代码实现解释
    • 5、常见问题

前言

这两天公司项目业务提出需求,要求在前端上传excel文件然后解析展示,因此写篇文章记录一下实现。

技术栈

springboot 2.6.6

1、引入依赖

maven格式:
	<dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>3.14</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>3.14</version>
    </dependency>

2、导入代码实现

package com.nanmu.payment.controller;

import com.nanmu.payment.common.MessageConst;
import com.nanmu.payment.entity.Result;
import com.nanmu.payment.pojo.OrderSetting;
import com.nanmu.payment.service.OrderSettingService;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * @author 
 * @description 
 * @date 2022/12/22
 **/
@RestController
@RequestMapping("/ordersetting")
@Slf4j
public class OrderSettingController {
    
    
    /**
     * 上传预约设置的excel文件
     * @param multipartFile
     * @return
     */
    @RequestMapping("/upload")
    public Result upload(@RequestParam("excelFile") MultipartFile multipartFile) {
        log.info("[预约设置-上传]fileName:{},size:{}", multipartFile.getOriginalFilename(), multipartFile.getSize());
        String filename = multipartFile.getOriginalFilename();
        if(StringUtils.isEmpty(filename)){
            return new Result(false,"缺少文件名");
        }
        //1 抽取excel数据 poi

        try (InputStream is = multipartFile.getInputStream();) {
            Workbook workbook = null;
            //1.1构造workbook
            if(filename.endsWith(".xls")){
                // excel 2003
                workbook = new HSSFWorkbook(is);
            }else if(filename.endsWith(".xlsx")){
                // excel 2007
                workbook = new XSSFWorkbook(is);
            }else{
                return new Result(false,"文件格式不正确,请检查重试");
            }

            //1.2遍历取数据
            List<OrderSetting> orderSettings = new ArrayList<>();
            for (Sheet sheet : workbook) {
                // 第0行为表头,直接从第1行取
                for (int i = 1; i <= sheet.getLastRowNum(); i++) {
                    Row row = sheet.getRow(i);
                    Date date = null;
                    int number = 0;
                    try {
                        date = null != row.getCell(0) ? row.getCell(0).getDateCellValue() : null;
                        number = null != row.getCell(1) ? Double.valueOf(row.getCell(1).getNumericCellValue()).intValue() : 0;
                    }catch (IllegalStateException|NumberFormatException e){
                        log.error("",e);
                        return new Result(false,String.format("数据格式错误,%s 第%d行",sheet.getSheetName(),row.getRowNum()+1));
                    }
                    if(null == date ){
                        return new Result(false,String.format("缺少必填数据,%s 第%d行",sheet.getSheetName(),row.getRowNum()+1));
                    }
                    //构造OrderSetting
                    orderSettings.add(new OrderSetting(date,number));
                }
            }
            log.info("[预约设置-上传]解析成功,result:{}",orderSettings);

            //2 调用service层
            //调用service层代码去掉
            return new Result(true,MessageConst.IMPORT_ORDERSETTING_SUCCESS);
        } catch (RuntimeException|IOException e) {
            log.info("",e);
            return new Result(false, MessageConst.IMPORT_ORDERSETTING_FAIL);
        }
    }
    
}

3、导出代码实现

3.1、准备导出文件模板

将导出文件模板放入resources包下面(如下图黄框所示)
在这里插入图片描述
文件内容如下图所示:
在这里插入图片描述

3.2、导出代码实现

package com.nanmu.payment.controller;

import com.nanmu.payment.common.MessageConst;
import com.nanmu.payment.entity.Result;
import com.nanmu.payment.service.MemberService;
import com.nanmu.payment.service.OrderService;
import com.nanmu.payment.service.ReportService;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 数据报告控制器
 */
@RestController
@RequestMapping("/report")
@Slf4j
public class ReportController {

    @Autowired
    private ReportService reportService;

    /**
     * 导出运营数据报表
     *
     * @return
     */
    @GetMapping("exportBusinessReport")
    public Result exportBusinessReport(HttpServletResponse response) {
        log.info("[导出运营数据报表]开始");
        try {
            //1-调用service层获取数据
            Map<String, Object> reportData = reportService.getBusinessReportData();
            log.debug("[导出运营数据报表]rpc rsp:{}", reportData);
            // 取出返回结果
            String reportDate = (String) reportData.get("reportDate");
            // 获取会员相关数据
            Long todayNewMember = (Long) reportData.get("todayNewMember");
            Long thisWeekNewMember = (Long) reportData.get("thisWeekNewMember");
            Long thisMonthNewMember = (Long) reportData.get("thisMonthNewMember");
            Long totalMember = (Long) reportData.get("totalMember");
            // 获取预约相关数据
            Long todayOrderNumber = (Long) reportData.get("todayOrderNumber");
            Long thisWeekOrderNumber = (Long) reportData.get("thisWeekOrderNumber");
            Long thisMonthOrderNumber = (Long) reportData.get("thisMonthOrderNumber");
            // 获取到诊相关数据
            Long todayVisitsNumber = (Long) reportData.get("todayVisitsNumber");
            Long thisWeekVisitsNumber = (Long) reportData.get("thisWeekVisitsNumber");
            Long thisMonthVisitsNumber = (Long) reportData.get("thisMonthVisitsNumber");
            // 获取套餐数据
            List<Map> hotSetmeal = (List<Map>) reportData.get("hotSetmeal");

            //2-读取模板构造workBook
            try (InputStream is = this.getClass().getClassLoader().getResourceAsStream("report_template.xlsx");
                 Workbook workbook = new XSSFWorkbook(is);
                 OutputStream os = response.getOutputStream()) {
                Sheet sheet = workbook.getSheetAt(0);
                //3-填入数据
                //日期
                sheet.getRow(2).getCell(5).setCellValue(reportDate);
                //会员统计数据
                sheet.getRow(4).getCell(5).setCellValue(todayNewMember);
                sheet.getRow(4).getCell(7).setCellValue(totalMember);
                sheet.getRow(5).getCell(5).setCellValue(thisWeekNewMember);
                sheet.getRow(5).getCell(7).setCellValue(thisMonthNewMember);
                //预约到诊数据统计
                sheet.getRow(7).getCell(5).setCellValue(todayOrderNumber);
                sheet.getRow(7).getCell(7).setCellValue(todayVisitsNumber);
                sheet.getRow(8).getCell(5).setCellValue(thisWeekOrderNumber);
                sheet.getRow(8).getCell(7).setCellValue(thisWeekVisitsNumber);
                sheet.getRow(9).getCell(5).setCellValue(thisMonthOrderNumber);
                sheet.getRow(9).getCell(7).setCellValue(thisMonthVisitsNumber);
                //热门套餐
                int rowNum = 12;
                for (Map setMeal : hotSetmeal) {
                    Row row = sheet.getRow(rowNum);
                    row.getCell(4).setCellValue((String) setMeal.get("name"));
                    row.getCell(5).setCellValue((Long) setMeal.get("setmeal_count"));
                    row.getCell(6).setCellValue(((BigDecimal) setMeal.get("proportion")).doubleValue());
                    rowNum++;
                }

                //4-写入网络输出流
                //写入返回流
                response.setContentType("application/vnd.ms-excel");
                response.setHeader("content-Disposition", "attachment;fileName=" + reportDate + "_report.xlsx");
                workbook.write(os);
                return null;
            } catch (IOException e) {
                // 不会出现/无法处理的受检异常,转换为运行时异常
                throw new RuntimeException(e);
            }
        } catch (RuntimeException e) {
            log.error("", e);
            return new Result(false, MessageConst.ACTION_FAIL);
        }

    }

}

4、代码实现解释

  • 导入前端上传的文件不进行存储,直接读取之后将内容存入数据库;
  • 导入支持.xls和.xlsx两种格式excel文件;
  • 导入文件行数不易过大,过大会出现内存泄漏等问题(后面再出关于百万行数据读取、导出的方案);
  • 导出文件使用模板,这样便于写入,同样不支持数据行过多;
  • 导出直接返回.xlsx格式excel文件给前端。

5、常见问题

  • 读取文件失败,提示信息:The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)
    分析及解决方案为:经分析发现是HSSFWorkbook不支持读取.xlsx,改为XSSFWorkbook即可。

  • 内存溢出问题,提示信息:java.lang.OutOfMemoryError: Java heap space
    at jdk.internal.reflect.GeneratedConstructorAccessor39.newInstance(Unknown Source) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:na]
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490) ~[na:na]
    at org.apache.xmlbeans.impl.schema.SchemaTypeImpl.createUnattachedNode(SchemaTypeImpl.java:1934) ~[xmlbeans-2.6.0.jar:na]
    at org.apache.xmlbeans.impl.schema.SchemaTypeImpl.createElementType(SchemaTypeImpl.java:1051) ~[xmlbeans-2.6.0.jar:na]
    at org.apache.xmlbeans.impl.values.XmlObjectBase.create_element_user(XmlObjectBase.java:938) ~[xmlbeans-2.6.0.jar:na]
    at org.apache.xmlbeans.impl.store.Xobj.getUser(Xobj.java:1675) ~[xmlbeans-2.6.0.jar:na]
    at org.apache.xmlbeans.impl.store.Xobj.find_all_element_users(Xobj.java:2098) ~[xmlbeans-2.6.0.jar:na]
    at org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTRowImpl.getCArray(Unknown Source) ~[poi-ooxml-schemas-3.14.jar:3.14]
    at org.apache.poi.xssf.usermodel.XSSFRow.(XSSFRow.java:73) ~[poi-ooxml-3.14.jar:3.14]
    at org.apache.poi.xssf.usermodel.XSSFSheet.initRows(XSSFSheet.java:215) ~[poi-ooxml-3.14.jar:3.14]
    at org.apache.poi.xssf.usermodel.XSSFSheet.read(XSSFSheet.java:178) ~[poi-ooxml-3.14.jar:3.14]
    at org.apache.poi.xssf.usermodel.XSSFSheet.onDocumentRead(XSSFSheet.java:165) ~[poi-ooxml-3.14.jar:3.14]
    at org.apache.poi.xssf.usermodel.XSSFWorkbook.parseSheet(XSSFWorkbook.java:417) ~[poi-ooxml-3.14.jar:3.14]
    at org.apache.poi.xssf.usermodel.XSSFWorkbook.onDocumentRead(XSSFWorkbook.java:382) ~[poi-ooxml-3.14.jar:3.14]
    at org.apache.poi.POIXMLDocument.load(POIXMLDocument.java:178) ~[poi-ooxml-3.14.jar:3.14]
    at org.apache.poi.xssf.usermodel.XSSFWorkbook.(XSSFWorkbook.java:279) ~[poi-ooxml-3.14.jar:3.14]
    分析及解决方案为:经分析发现是导入、导出数据行数太多,目前建议业务分批导出,后续出百万行数据读取、导出的方案。

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

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

相关文章

抖音小程序实践三:接口开发指南

通过官方文档可以更系统的学习到所有的接口&#xff0c;我这边罗列一下我自己用到测试过的接口供大家参考。 前端-小程序对接官方文档&#xff1a;https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/api/open-interface/user-information/tt-get-user-info服务端-小…

个人黄金准备与须知

投资黄金可以为自己的资产保值&#xff0c;也可以福泽后人&#xff0c;因此十分符合国人传统文化特点和理财智慧。可是现在市场中可以投资黄金的平台那么多&#xff0c;投资者必须先认真“调查研究”&#xff0c;才能“去芜存菁”&#xff0c;选到值得托付的好平台。 其实投资者…

为笔记本电脑绑定公网IP随时随地BT做种完整方案(frp加v2ray配合比特彗星点亮绿灯)

BT做种需要拥有固定的IP(IPV4)和端口&#xff0c;如果想在拥有固定IP的服务端做种&#xff0c;可以直接参考前一期https://www.v2fy.com/p/2022-12-25-bt-1671963832000/ &#xff0c;如果想使用笔记本随时随地做种&#xff0c;那就需要拥有固定IP的服务器进行流量转发。 本篇…

富特科技在创业板IPO过会:计划募资约9亿元,股东包括小米等

近日&#xff0c;深圳证券交易所披露的信息显示&#xff0c;浙江富特科技股份有限公司&#xff08;下称“富特科技”&#xff09;获得创业板上市委会议审核通过。据贝多财经了解&#xff0c;富特科技于2022年6月16日在创业板递交招股书。 本次冲刺创业板上市&#xff0c;富特科…

【聆思CSK6 视觉AI开发套件试用】头肩、手势识别体验与PWM舵机控制

本篇文章来自极术社区与聆思科技组织的CSK6 视觉AI开发套件活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;酷电玩家 环境搭建 官方文档详细环境搭建教程&#xff1a;环境搭建 1、下载Git进行安装。 2、安装lisa zep工具&#xff0c;并初始化 CSK6…

洛谷千题详解 | P1026 [NOIP2001 提高组] 统计单词个数【C++、Java语言】

博主主页&#xff1a;Yu仙笙 专栏地址&#xff1a;洛谷千题详解 目录 题目描述 输入格式 输出格式 输入输出样例 解析&#xff1a; C源码&#xff1a; Java源码&#xff1a; C源码2&#xff1a; ----------------------------------------------------------------------------…

vector模板的简易实现

这篇文章&#xff0c;我们模拟一下STL里面的vector的实现。但是会简化一些内容&#xff0c;让大家能够更好的理解。模拟实现的目的不是为了更好的造轮子&#xff0c;而是为了更好的理解这些容器。 文章目录1. 成员变量2. push_back函数3. reserve函数4. pop_back函数和下标运算…

QT 多线程中使用QCanBusDevice进行PCAN通讯时,无法正常发出数据

QT 多线程中使用QCanBusDevice进行PCAN通讯时&#xff0c;无法正常发出数据 前言 我一开始的代码逻辑是&#xff0c;PCAN开启、关闭、发送、接收这些功能整合在一个工具类中&#xff0c;这个工具类的对象是在主线程创建的&#xff0c;然后我有一个要循环定时发送的功能是独立…

与企企通强强联手!哈尔斯二期数字化采购项目正式启动

近日&#xff0c;浙江哈尔斯真空器皿股份有限公司&#xff08;以下简称“哈尔斯”&#xff09;联合企企通举办二期数字化采购项目启动会&#xff0c;旨在助力哈尔斯实现采购数字化全面升级&#xff0c;提升自主品牌竞争力。会上&#xff0c;双方就该项目的建设方案、项目资源、…

铝合金表面处理废水除铝工艺

铝型材表面处理用水量大&#xff0c;产生废水多&#xff0c;废水中有害物质持续排放。如不加以处理必将污染环境。同时伴随着我国对排污量的征税&#xff0c;也会增加企业的成本和负担。因此&#xff0c;从企业的社会责任和效益两方面考虑&#xff0c;进行废水处理是必须和必要…

解决VsCode启动Vue项目报错:‘vue-cli-service‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。

问题描述 最近居家办公&#xff0c;网速不太稳定&#xff0c;开会的时候网络也是断断续续的&#xff0c;今天需要拉下前端项目运行起来 在我执行npm i下载包的时候&#xff0c;我看到网络超时的错误警告就感觉不太秒。知道大概率要启动失败了 果不其然执行npm run serve的时…

窃取信息的新恶意软件通过假冒的破解网站感染使用者

©网络研究院 一种名为“RisePro”的新型信息窃取恶意软件正在通过由 PrivateLoader 按安装付费 (PPI) 恶意软件分发服务运营的虚假破解站点进行分发。 RisePro 旨在帮助攻击者从受感染的设备中窃取受害者的信用卡、密码和加密钱包。 本周Flashpoint 和 Sekoia的分析师发…

前端框架 Nuxt3 集成 Pinia

目录 一、Nuxt3集成Pinia 二、Pinia的使用 state的使用 1、基本使用及动态渲染 2、state的重置 3、批量更改state数据 getters的使用 1、getters的基本使用 2、getters传参 actions的使用 1、actions的基本使用 一、Nuxt3集成Pinia 参考官方文档&#xff1a;简介 |…

【JavaSE】常用类(447~515)

String 447.常用类-每天一考 1.画图说明线程的生命周期&#xff0c;以及各状态切换使用到的方法等 状态&#xff0c;方法 2.同步代码块中涉及到同步监视器和共享数据&#xff0c;谈谈你对同步监视器和共享数据的理解&#xff0c;以及注意点。 synchronized(同步监视器){//操…

消息队列RabbitMQ学习笔记(五)高级特性

1. 发布确认高级 在生产环境中由于一些不明原因&#xff0c;导致 RabbitMQ 重启&#xff0c;在 RabbitMQ 重启期间生产者消息投递失败&#xff0c; 导致消息丢失&#xff0c;需要手动处理和恢复。于是&#xff0c;我们开始思考&#xff0c;如何才能进行 RabbitMQ 的消息可靠投…

ccc-sklearn-11-线性回归(1)

1.线性回归概述 回归需求在现实中非常多&#xff0c;自然也有了各种回归算法。最著名的就是线性回归和逻辑回归&#xff0c;衍生出了岭回归、Lasso、弹性网&#xff0c;以及分类算法改进后的回归&#xff0c;如回归树、随机森林回归、支持向量回归等&#xff0c;一切基于特征预…

自定义卷积实现卷积的重参数【手撕代码】

在我的上篇文章中主要对RepVGG进行了解析【RepVGG网络中重参化网络结构解读】&#xff0c;里面详细的对论文中的代码进行了解析&#xff0c;展示了RepVGG在重参数时是如何将训练分支进行合并的&#xff0c;总的一句话就是在推理阶段&#xff0c;会将1x1以及identity分支以paddi…

vivo 游戏中心低代码平台的提效秘诀

作者&#xff1a;vivo 互联网服务器团队- Chen Wenyang 本文根据陈文洋老师在“2022 vivo开发者大会"现场演讲内容整理而成。公众号回复【2022 VDC】获取互联网技术分会场议题相关资料。 在互联网流量见顶和用户需求分层的背景下&#xff0c;如何快速迭代产品功能&#xf…

函数模板-C11/17/14

函数模板 文章目录函数模板定义函数模板使用函数模板样例两阶段翻译 Two-Phase Translation模板的编译和链接问题多模板参数引入额外模板参数作为返回值类型让编译器自己找出返回值类型将返回值声明为两个模板参数的公共类型样例默认模板参数样例重载函数模板模板函数特化非类型…

cocoapods的使用

swift开发之cocoapods的使用 之前介绍了cocoapods的使用&#xff0c;我们可以知道通过pod search XXX(三方依赖库名称)可以就搜索到想要的第三方是否存在。 这次主要简单介绍cocoapods如何引入第三方库的,以BluetoothKit为例。 首先&#xff0c;我们终端中通过cd命令定位到要…