Java POI (2)—— Excel文件的上传与导出(实例演示)

一、单文件的上传功能

         这里是一个demo的流程图,下面按照这个流程图做了一个简单的实现,有部分判断没有加上,实际操作中,可以根据自己的需求进行增加或者修改。并且此处还是在接受文件传入后将文件进行了下载,保存到本地的操作,这个要按照具体情况具体分析,看需求是否要把上传进来的文件进行数据备份,或者直接以流的形式读进来,然后进行解析等逻辑操作后,最后关闭释放流也可以,没有说一定要上传完文件就要下载下来。

controller层 (接受前端传入的文件参数,为单个文件)

@RequestMapping( "/salary/server/excelxz")
public class SalaryExcelOperatController {@Autowiredprivate SalaryExcelOperatService salaryexcelOperatService;@PostMapping("/upload")public RespondDto uploadFile(@RequestParam("file") MultipartFile multipartFile) {SalaryExcelOperatVo excelOperatVo =salaryexcelOperatService.uploadExcel(multipartFile);return new RespondDto(excelOperatVo);
}

service层 (这里使用了easyExcel的方式实现了,一个Excel中包含多个sheet的读取操作,但本质和POI的思想是差不多的 )

@Service
@Slf4j
public class SalaryExcelOperatServiceImpl implements SalaryExcelOperatService {@Resourceprivate SalaryExcelOperatMapper salaryexcelOperatMapper;@Resourceprivate LoginMapper loginMapper;/*** 上传 Excel 文件* 1、接收文件,保存到本地;* 2、获取本地文件路径调用 readExcel 读成ArrayList;*/@Overridepublic SalaryExcelOperatVo uploadExcel(MultipartFile multipartFile) {if (multipartFile==null) {log.error("文件不能为空");throw new RuntimeException("上传Excel文件内容为空,请重新上传!");}String fileName = multipartFile.getOriginalFilename();//判断文件是否是excel文件assert fileName != null;if (!fileName.endsWith("xls") && !fileName.endsWith("xlsx")) {log.error(fileName + "不是Excel文件!");throw new RuntimeException(fileName + "不是Excel文件!");}//保存文件到本地File dir1 = new File("/roots/uploadFile/xzExcel");if (!dir1.exists()) {dir1.mkdirs();}//统一日期格式LocalDateTime current = LocalDateTime.now();DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");String formatted = current.format(formatter);//加上三位随机数Random random = new Random();int end3 = random.nextInt(999);File file1 = new File(dir1.getAbsolutePath() + File.separator + formatted + "-" + end3 + "-" + multipartFile.getOriginalFilename());try {multipartFile.transferTo(file1);} catch (IOException e) {e.printStackTrace();}//创建返回对象SalaryExcelOperatVo的实例化对象: resultSalaryExcelOperatVo result = new SalaryExcelOperatVo();//获取excel文件sheet1 的内容ArrayList<InSalary> inSalaries1 = readExcel1(file1.getAbsolutePath());ArrayList<SalaryStaffPerOneListVo> vo1 = new ArrayList<>();SalaryStaffPerOneListVo oneListVo ;for(InSalary inSalary1:inSalaries1){oneListVo = new SalaryStaffPerOneListVo();BeanUtils.copyProperties(inSalary1,oneListVo);vo1.add(oneListVo);
}result.setSheetOne(vo1);//获取excel文件sheet2 的内容ArrayList<InSalary> inSalaries2 = readExcel2(file1.getAbsolutePath());ArrayList<SalaryStaffPerTwoListVo> vo2 = new ArrayList<>();SalaryStaffPerTwoListVo twoListVo ;for(InSalary inSalary2:inSalaries2){twoListVo = new SalaryStaffPerTwoListVo();BeanUtils.copyProperties(inSalary2,twoListVo);vo2.add(twoListVo);}result.setSheetTwo(vo2);return result;}/*** 抽离出   【读取excel文件,包含多个sheet内容的方法】* 1、工作区间 -> Sheet -> cell;* 2、每行为一个 inSalary 对象, 把所有对象用 ArrayList保存;* 从第二行开始读取,第一行表头忽略;** @param filePath ;本地保存后的文件地址* @return ArrayList<InSalary>*/private ArrayList<InSalary> readExcel1(String filePath) {ArrayList<InSalary> inSalary = new ArrayList<>();// 该监听将excel文件一行一行读入内存(必须有)SalaryExcelListener listener = new SalaryExcelListener();ExcelReader excelReader = EasyExcel.read(filePath, InSalary.class,listener).build();ReadSheet readSheet = EasyExcel.readSheet(0).build();excelReader.read(readSheet);// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的excelReader.finish();// readList 文件中的数据,不包括表头inSalary .addAll(listener.getList());return inSalary;}private ArrayList<InSalary> readExcel2(String filePath) {ArrayList<InSalary> inSalary = new ArrayList<>();// 该监听将excel文件一行一行读入内存(必须有)SalaryExcelListener listener = new SalaryExcelListener();ExcelReader excelReader = EasyExcel.read(filePath, InSalary.class,listener).build();ReadSheet readSheet = EasyExcel.readSheet(1).build();excelReader.read(readSheet);// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的excelReader.finish();// readList 文件中的数据,不包括表头inSalary .addAll(listener.getList());return inSalary;}

二、多文件的上传功能 

controller层 (接受前端传入的文件参数,为多个文件)

    @PostMapping(value = "/uploadExcels")public RespondDto upLoadFiles(@NonNull @RequestParam("multipartFiles") MultipartFile[] multipartFiles,@NonNull @RequestParam("types") String[] types){return fileService.upLoadFiles(multipartFiles,types);}

        这里需要说明一点,controller层的实现方式有很多中,没有说当要传入多个文件的时候一定要去使用数组的形式进行 ,在这样写之前我也尝试了其他的两种写法:

    @PostMapping("/upload")public String upload(@RequestParam("files") List<MultipartFile> files, HttpServletRequest request) {for (MultipartFile file : files) {String type1 = request.getParameter("type1");String type2 = request.getParameter("type2");// 根据type1和type2的值,对上传的文件进行特定处理// ...}return "upload success";}
    @PostMapping(value = "/uploadExcels")public RespondDto upLoadFiles(@NonNull @RequestParam("multipartFiles") List<MultipartFile> multipartFiles,@NonNull @RequestParam("types") List<MultipartFile> types){return fileService.upLoadFiles(multipartFiles,types);}

 不过实际中具体怎么写,还是看跟前端的沟通以及最终的接口文档来进行操作。

service层 (这里实现了将多个Excel一次性上传后,后端将这些Excel文件进行接收并保存到本地,并且将每个文件的名字以文件类型的名字进行重命名)

@Override
public RespondDto upLoadFiles(MultipartFile[] files, String[] types) {long MAX_SIZE = 1024 * 1024 * 10;// 文件大小限制为10MBLocalDateTime current = LocalDateTime.now();DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");String formattedDate = current.format(formatter);//加上三位随机数Random random = new Random();int randomNum = random.nextInt(999);File dir = new File("/manage/uploadFile/"+formattedDate+randomNum);if (!dir.exists()) {dir.mkdirs();}for (int i = 0; i < files.length; i++) {MultipartFile file = files[i];String type = types[i];// 判断文件或类型不能为空if (file.isEmpty() || StringUtils.isEmpty(type)) {return new RespondDto<>("文件或类型不能为空,请重新上传!");}// 判断文件大小是否合适if (file.getSize() > MAX_SIZE) {return new RespondDto<>("文件过大,上传失败!");}String originalFileName = file.getOriginalFilename();// 获取文件名和扩展名String fileExt = originalFileName.substring(originalFileName.lastIndexOf("."));String fileName = type ;File fileToSave = new File(dir.getAbsolutePath() + File.separator + fileName +fileExt);try {//文件写入 transferTofile.transferTo(fileToSave);String fileUrl =  fileToSave.getAbsolutePath();fileUrls.add(fileUrl);} catch (IOException e) {e.printStackTrace();return new RespondDto<>("文件上传失败!");}log.info("【上传文件】"+fileName+" 已保存到本地:{}", originalFileName, fileToSave.getAbsolutePath());}return new RespondDto<>(fileUrls);
}

 在postman中进行测试,结果为:

三、文件导出功能

①、导出带有图片的Excel

@RestController
@RequestMapping( "/salary/server/excel")
@Slf4j
public class ExportjxExcelsController {@Value("#{'${headerTitles}'.split(',')}")private String[] headerTitles;@AutowiredStaffMapper staffMapper;@GetMapping("/jxdownload")public void export( HttpServletResponse response ,Integer year,Integer month) throws IOException {//设置响应response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");// 设置要导出的文件名, 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系String fileName= URLEncoder.encode("XXXX数据","UTF-8").replaceAll("\\+","%20");response.setHeader("Content-disposition","attachment;filename*=utf-8''"+fileName+".xlsx");// 从数据库ensure表中读取数据List<Salary> userList =  staffMapper.getAllStaff(year, month);log.info("数据为:\n{}", userList);List<Map<String, String>> salaryList = new ArrayList<>();for (Salary salary : userList) {Map<String, String> salaryMap = new LinkedHashMap<>();salaryMap.put("userName", salary.getUserName());salaryMap.put("firstDepart", salary.getFirstDepart());salaryMap.put("secondDepart", salary.getSecondDepart());salaryMap.put("post", salary.getPost());;salaryMap.put("careDeduct", salary.getCareDeduct());salaryMap.put("personalTax", salary.getPersonalTax());salaryMap.put("actualPay", salary.getActualPay());salaryMap.put("socialUnitpart", salary.getSocialUnitpart());salaryMap.put("amonthlySalary", salary.getAmonthlySalary());salaryMap.put("achieveBonus", salary.getAchieveBonus());salaryMap.put("status", Integer.valueOf(103).equals(salary.getStatus()) ? "已确认" : "未确认");salaryMap.put("evidence", salary.getEvidence());salaryList.add(salaryMap);}//取出map键值对中的value值List<String> valueList = new ArrayList<>();for (Map<String, String> salaryMap : salaryList) {Set<Map.Entry<String, String>> entrySet = salaryMap.entrySet();for (Map.Entry<String, String> entry : entrySet) {valueList.add(entry.getValue());}}//  保存文件到本地File dir = new File("/roots/uploadFile/exportxzExcel");if (!dir.exists()) {dir.mkdirs();}//加上三位随机数Random random = new Random();int end3 = random.nextInt(999);String path = dir.getAbsolutePath() + File.separator + end3  +fileName + ".xlsx";log.info("path文件路径为:{}",path);SSExcel07Workbook ssExcel07Workbook = (new SSExcel07Workbook()).openOrCreate(path);SSExcel07Sheet sheet = ssExcel07Workbook.createSheet("sheet1");// 创建Excel表格头部(有43个字符串)//将表头数据写入到单元格中int col = 0;for (String title : headerTitles) {SSExcel07Cell cell = sheet.getCellOrCreate(0, col);cell.setCellValue(title);col++;}// 将数据写入数据到Excel文件中// 循环遍历 salaryList 中的值,写入到 Excel 文件中int row = 1;int col1 = 0;for (String strval : valueList) {// 判断当前列是否超过了最大列数,如果超过了,则重置列数,同时行数加1if (col1 >= headerTitles.length) {col1 = 0;row++;}if(col1==42){//在最后一列插入图片SSExcel07Cell imgCell = sheet.getCellOrCreate(row, 42);imgCell.insertImg(strval, 0, 0);}else {// 获取当前单元格SSExcel07Cell cell = sheet.getCellOrCreate(row, col1);// 将当前单元格的值设置为当前的 strval 值cell.setCellValue(strval);}// 将列数加1col1++;}ssExcel07Workbook.getXSSFWorkBook().write(response.getOutputStream());log.info("文件导出完成!");}
}

②、将之前上传的多个Excel按照一定的映射规则生成一个Excel文件(映射规则可以写在配置文件中,也可以写到数据库中,主要看映射关系的改动频率是多少)

@Overridepublic RespondDto writeExcel(String[] fileUrls) throws IOException {long startTime=System.currentTimeMillis (); //获取开始时间String fileUrl = null;for (String url : fileUrls) {fileUrl = url;}// 读取配置文件,configFile 是指配置文件的路径和名称。Properties config = new Properties();ClassPathResource configInputStream = new ClassPathResource("config.properties");config.load(configInputStream.getInputStream());//  保存文件到本地File excelDir = new File("/manage/uploadFile");if (!excelDir.exists()) {excelDir.mkdirs();}LocalDateTime current = LocalDateTime.now();DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");String formattedDate = current.format(formatter);//加上三位随机数Random random = new Random();int randomNum = random.nextInt(999);// 获取文件名String destExcelFileName = formattedDate + "-" + randomNum + "-" ;FileInputStream destinationFileInputStream = new FileInputStream(excelDir + destExcelFileName);Workbook destinationWorkbook = new XSSFWorkbook(destinationFileInputStream);Map<String, Workbook> workbookMap = new HashMap<>();for (String key : config.stringPropertyNames()) {String[] destArray = org.springframework.util.StringUtils.tokenizeToStringArray(key, ".");String[] srcArray = org.springframework.util.StringUtils.tokenizeToStringArray(config.getProperty(key), ".");System.out.println(srcArray[0]);if (destArray == null || srcArray == null || srcArray.length != 3 || destArray.length != 2) {continue;}Workbook sourceWorkbook = null;if (workbookMap.containsKey(srcArray[0])) {sourceWorkbook = workbookMap.get(srcArray[0]);} else {// 读取源文件//FileInputStream sourceFileInputStream = new FileInputStream(excelDir + srcArray[0] + ".xlsx");FileInputStream sourceFileInputStream = new FileInputStream(fileUrl);sourceWorkbook = new XSSFWorkbook(sourceFileInputStream);workbookMap.put(srcArray[0], sourceWorkbook);}Sheet sourceSheet = sourceWorkbook.getSheet(srcArray[1]);CellReference sourceCellRef = new CellReference(srcArray[2]);Row sourceRow = sourceSheet.getRow(sourceCellRef.getRow());Cell sourceCell = null;if (sourceRow != null) {sourceCell = sourceRow.getCell(sourceCellRef.getCol());}Sheet destinationSheet = destinationWorkbook.getSheet(destArray[0]);CellReference destCellRef = new CellReference(destArray[1]);Row destRow = destinationSheet.getRow(destCellRef.getRow());if (destRow == null) {destRow = destinationSheet.createRow(destCellRef.getRow());}Cell destCell = destRow.createCell(destCellRef.getCol());// 执行 copy 方法copyCellValue(sourceCell, destCell);}// 保存目标文件FileOutputStream outputStream = new FileOutputStream(excelDir + destExcelFileName);destinationWorkbook.write(outputStream);// 关闭资源outputStream.close();destinationFileInputStream.close();destinationWorkbook.close();workbookMap.values().forEach(k -> {try {k.close();} catch (IOException e) {}});long endTime=System.currentTimeMillis (); //获取结束时间System.out.println ( "程序运行时间: " + (endTime-startTime)/1000 + "s" );return new RespondDto<>("Excel导出成功!");}/*** copyCellValue() 方法用于将源单元格的值复制到目标单元格中。* @param sourceCell 是源单元格对象* @param destCell 是目标单元格对象*/public static void copyCellValue(Cell sourceCell, Cell destCell) {// 如果 sourceCell 和 destCell 中任意一个为 null,则不进行操作,方法直接返回。if (sourceCell == null || destCell == null) {return;}// 首先,将源单元格的单元格样式克隆到目标单元格上,然后再将源单元格的值赋给目标单元格。CellStyle sourceStyle = sourceCell.getCellStyle();CellStyle destinationStyle = destCell.getSheet().getWorkbook().createCellStyle();destinationStyle.cloneStyleFrom(sourceStyle);destCell.setCellStyle(destinationStyle);// 如果源单元格的数据类型为字符串类型,则复制字符串值;如果为布尔类型,则复制布尔值;如果为公式类型,则复制公式字符串;如果为数字类型,则复制数字值。if (sourceCell.getCellTypeEnum().equals(CellType.STRING)) {destCell.setCellValue(sourceCell.getStringCellValue());} else if (sourceCell.getCellTypeEnum().equals(CellType.BOOLEAN)) {destCell.setCellValue(sourceCell.getBooleanCellValue());} else if (sourceCell.getCellTypeEnum().equals(CellType.FORMULA)) {if (DateUtil.isCellDateFormatted(sourceCell)) {destCell.setCellValue(sourceCell.getDateCellValue());} else {destCell.setCellValue(sourceCell.getNumericCellValue());}} else if (sourceCell.getCellTypeEnum().equals(CellType.NUMERIC)) {if (DateUtil.isCellDateFormatted(sourceCell)) {destCell.setCellValue(sourceCell.getDateCellValue());} else {destCell.setCellValue(sourceCell.getNumericCellValue());}// 如果源单元格是空的,则在目标单元格中写入一个空串("")。} else if (sourceCell.getCellTypeEnum().equals(CellType.BLANK)) {destCell.setCellValue("");}}

四、注意事项

①、在使用poi的时候一定要注意,选对依赖的版本

不能想用什么依赖的版本就使用什么依赖版本,有些方法,不同的版本之间相差还是比较大的,此处我使用的版本就是3.17的,如果你使用3.9版本的依赖,就不能运行成功。

        <!--xlsx(07)--><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.17</version></dependency>

②、当在上传多个文件的时候,出现 the request was rejected because its size (10821303) exceeds the configured maximum (10485760)的报错

        这是因为springboot默认配置 multipart.max-file-size大小是1M,max-request-size默认大小是10M  故可以在yml文件中添加:

spring:servlet:multipart:max-file-size: 100MBmax-request-size: 500MB

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

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

相关文章

分类预测 | MATLAB实现GA-LSTM遗传算法优化长短期记忆网络的数据多输入分类预测

分类预测 | MATLAB实现GA-LSTM遗传算法优化长短期记忆网络的数据多输入分类预测 目录 分类预测 | MATLAB实现GA-LSTM遗传算法优化长短期记忆网络的数据多输入分类预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 MATLAB实现GA-LSTM遗传算法优化长短期记忆网络的数据多…

douyin23.9 deviceid和iid设备注册分析

使用23.9版本进行注册&#xff08;版本多少 其实没有那么重要&#xff09; 老生常谈&#xff0c;老规矩注册接口device_register不能少吧&#xff0c;然后要检测设备app_alert_check吧&#xff0c;之后要发app_log日志包吧。 当然除了只有这些接口肯定是不行啦&#xff0c;加密…

蓝牙资讯|未来几年物联网迅猛发展,蓝牙发挥重要作用

IDC预测&#xff0c;2023年全球物联网(IoT)支出将达到8057亿美元&#xff0c;比2022年增长10.6%。物联网生态系统的投资预计将在2026年超过1万亿美元&#xff0c;在2023-2027年的预测期内&#xff0c;复合年增长率(CAGR)为10.4%。 到2023年&#xff0c;物联网服务将成为最大的…

【Java EE】-博客系统二(前后端分离)

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀冬瓜的主页&#x1f319; 专栏&#xff1a;【JavaEE】 分享: 徘徊着的 在路上的 你要走吗 易碎的 骄傲着 那也曾是我的模样 ——《平凡之路》 主要内容&#xff1a;显示用户信息、上传头像、新增博客、删除博客、修改博客…

Java HelloWorld

一、java命令 目录 一、java命令 二、Java HelloWorld 1.单个java文件 2.单个包多java文件 3.多个包 三、jar包 1.生成jar包 2.引用jar包 三、IntelliJ IDEAMaven HelloWorld 四、IntelliJ IDEAMavenspringboot HelloWorld javac&#xff1a;将.java文件编译成.clas…

设计模式篇(Java):适配器模式

设计模式篇(Java)&#xff1a;建造者模式 八、适配器模式 8.1 适配器模式基本介绍 生活中的适配器例子 比如生活中的插座&#xff0c;在不同国家插座有着不同的规格&#xff0c;如果我们从一个国家去另外一个国家需要使用插座时就需要一个中间转换器把两种不同规则的插座适配一…

Spring Security系列之基础概念

文章目录 基本原理Authentication接口UserDetailsService 接口PasswordEncoder 接口EnableWebSecurity 注解 基本原理 SpringSecurity 本质是一个过滤器链&#xff0c;采用的是责任链设计模式。 启动的时候&#xff0c;控制台打印出来的 DefaultSecurityFilterChain 过滤器&…

openfeign实现远程调用

一 openfeign简介 Feign 是声明性(注解)web 服务客户端它使编写 web 服务客户端更加容易请创建一个接口并对其进行注解.它具有可插入注解支持&#xff0c;包括Feign注解和JAXRS注解Feign 还支持可插拔编码器和解码器。Spring cloud 添加了对Spring MVC注解的支持&#xff0c;并…

GPT-4 参加2023年高考数学,人工智能对话机器人和人类对决,快谁速度快

ChatGPT从入门到精通&#xff0c;一站式掌握办公自动化/爬虫/数据分析和可视化图表制作 全面AI时代就在转角 道路已经铺好了 “局外人”or“先行者” 就在此刻 等你决定 让ChatGPT帮你高效实现职场办公&#xff01;行动起来吧。 【点击查看更多知识】ChatGPT从入门到精通&am…

使用SSH远程直连Docker容器

文章目录 1. 下载docker镜像2. 安装ssh服务3. 本地局域网测试4. 安装cpolar5. 配置公网访问地址6. SSH公网远程连接测试7.固定连接公网地址8. SSH固定地址连接测试 转载自cpolar极点云文章&#xff1a;SSH远程直连Docker容器 在某些特殊需求下,我们想ssh直接远程连接docker 容器…

【STM32智能车】电机控制

【STM32智能车】电机控制 PWMPWM基本用法&#xff1a; 电机驱动基本控制基本状态 欢迎收看由咸鱼菌工作室出品的STM32系列教程。本篇内容主要电机控制 PWM 我们要控制电机&#xff0c;就要先了解一下PWM。 PWM(Pulse Width Modulation)控制——脉冲宽度调制技术&#xff0c;通…

2022前端趋势报告(下)

前端博主&#xff0c;热衷各种前端向的骚操作&#xff0c;经常想到哪就写到哪&#xff0c;如果有感兴趣的技术和前端效果可以留言&#xff5e;博主看到后会去代替大家踩坑的&#xff5e; 主页: oliver尹的主页 格言: 跌倒了爬起来就好&#xff5e; 一、前言 本文内容来自于《St…