Java实现Excel导入导出

article/2025/9/9 7:26:45

一、导入

前言:导入必须用post请求

具体原因在2中叙述

1、Excel导入

总结一下目标,就是要将excel中的数据行、逐一提取,最后得到一个list,这个list的每个元素就是excel的每个数据行的实例,之后的操作就是常规的java逻辑处理了。

可以把这个过程分为三步:

1)接收数据
后台使用 MultipartFile 接收文件数据(这里复习一下,springmvc接收参数的几种方式)。

前端如果是前后端分离项目我们不用管,
如果是jsp项目可以用文件标签<input type="file" name="" value=""/>传参。

2)提取数据
提取数据的底层是用IO流实现的,我们这里使用封装好的工具类。excel工具类有很多、很多,我一般是使用适配性最好的,不然一会儿springboot项目、一会儿spring项目等等,还得换不同的工具类;效率什么的不是首要考虑项。
使用工具类后,我们就得到了一个List<List<String>>:Excel的每个数据行组成一个List<String>,多个数据行就组成了List<List<String>>

这里有一个小坑,说明一下;有时候你导入的数据,如果是数字比如32位的卡号等等,用工具类提取出来成了科学计数法、或者后面加了小数点;这就说明你用的这个工具类没有将数字类型数据进行处理,你需要在工具类中找到数字类型,添加toText()方法。
当然你也可以使用我后面提供的工具类,这个问题已经作了处理。

3)将list的元素处理成实例对象,方便后续处理
在作转换的时候,还可以加一些校验、限制,比如限制excel导入总行数不得超过多少、限制某列参数不能重复等等。

下面提供一下我用的excel导入工具类,springboot或者spring项目都可以用

public class ImportExeclUtil {private static int totalRows = 0;// 总行数private static int totalCells = 0;// 总列数private static String errorInfo;// 错误信息/** 无参构造方法 */public ImportExeclUtil(){}public static int getTotalRows(){return totalRows;}public static int getTotalCells(){return totalCells;}public static String getErrorInfo(){return errorInfo;}/**** 根据流读取Excel文件*** @param inputStream* @param isExcel2003* @return* @see [类、类#方法、类#成员]*/public List<List<String>> read(InputStream inputStream, boolean isExcel2003)throws IOException{List<List<String>> dataLst = null;/** 根据版本选择创建Workbook的方式 */Workbook wb = null;if (isExcel2003){wb = new HSSFWorkbook(inputStream);}else{wb = new XSSFWorkbook(inputStream);}dataLst = readDate(wb);return dataLst;}/**** 读取数据** @param wb* @return* @see [类、类#方法、类#成员]*/private List<List<String>> readDate(Workbook wb){List<List<String>> dataLst = new ArrayList<List<String>>();/** 得到第一个shell */Sheet sheet = wb.getSheetAt(0);/** 得到Excel的行数 */totalRows = sheet.getPhysicalNumberOfRows();/** 得到Excel的列数 */if (totalRows >= 1 && sheet.getRow(0) != null){totalCells = sheet.getRow(0).getPhysicalNumberOfCells();}/** 循环Excel的行 */for (int r = 1; r < totalRows; r++){Row row = sheet.getRow(r);if (row == null){continue;}List<String> rowLst = new ArrayList<String>();/** 循环Excel的列 */for (int c = 0; c < getTotalCells(); c++){Cell cell = row.getCell(c);String cellValue = "";if (null != cell){// 以下是判断数据的类型switch (cell.getCellTypeEnum()){case NUMERIC: // 数字//如果是日期的话if(cell != null && HSSFDateUtil.isCellDateFormatted(cell)){Date d = cell.getDateCellValue();DateFormat formater = new SimpleDateFormat("yyyy/MM/dd");String da = formater.format(d);cellValue = da;break;}cellValue = NumberToTextConverter.toText(cell.getNumericCellValue());break;case STRING: // 字符串cellValue = cell.getStringCellValue();break;case BOOLEAN: // BooleancellValue = cell.getBooleanCellValue() + "";break;case FORMULA: // 公式cellValue = cell.getCellFormula() + "";break;case BLANK: // 空值cellValue = "";break;case ERROR: // 故障cellValue = "非法字符";break;default:cellValue = "未知类型";break;}}rowLst.add(cellValue);}/** 保存第r行的第c列 */dataLst.add(rowLst);}return dataLst;}/**** 根据Excel表格中的数据判断类型得到值** @param cell* @return* @see [类、类#方法、类#成员]*//*private static String getCellValue(Cell cell){String cellValue = "";if (null != cell){// 以下是判断数据的类型switch (cell.getCellType()){case HSSFCell.CELL_TYPE_NUMERIC: // 数字;: // 数字if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)){Date theDate = cell.getDateCellValue();SimpleDateFormat dff = new SimpleDateFormat("yyyy-MM-dd");cellValue = dff.format(theDate);}else{DecimalFormat df = new DecimalFormat("0");cellValue = df.format(cell.getNumericCellValue());}break;case HSSFCell.CELL_TYPE_STRING: // 字符串cellValue = cell.getStringCellValue();break;case HSSFCell.CELL_TYPE_BOOLEAN: // BooleancellValue = cell.getBooleanCellValue() + "";break;case HSSFCell.CELL_TYPE_FORMULA: // 公式cellValue = cell.getCellFormula() + "";break;case HSSFCell.CELL_TYPE_BLANK: // 空值cellValue = "";break;case HSSFCell.CELL_TYPE_ERROR: // 故障cellValue = "非法字符";break;default:cellValue = "未知类型";break;}}return cellValue;}*//**** 根据实体成员变量的类型得到成员变量的值** @param realValue* @param fields* @param f* @param cellValue* @return* @see [类、类#方法、类#成员]*/private static Object getEntityMemberValue(Object realValue, Field[] fields, int f, String cellValue){String type = fields[f].getType().getName();switch (type){case "char":case "java.lang.Character":case "java.lang.String":realValue = cellValue;break;case "java.util.Date":realValue = StringUtils.isBlank(cellValue) ? null : DateUtil.strToDate(cellValue, DateUtil.YYYY_MM_DD);break;case "java.lang.Integer":realValue = StringUtils.isBlank(cellValue) ? null : Integer.valueOf(cellValue);break;case "int":case "float":case "double":case "java.lang.Double":case "java.lang.Float":case "java.lang.Long":case "java.lang.Short":case "java.math.BigDecimal":realValue = StringUtils.isBlank(cellValue) ? null : new BigDecimal(cellValue);break;default:break;}return realValue;}/**** 根据路径或文件名选择Excel版本*** @param filePathOrName* @param in* @return* @throws IOException* @see [类、类#方法、类#成员]*/public static Workbook chooseWorkbook(String filePathOrName, InputStream in)throws IOException{/** 根据版本选择创建Workbook的方式 */Workbook wb = null;boolean isExcel2003 = ExcelVersionUtil.isExcel2003(filePathOrName);if (isExcel2003){wb = new HSSFWorkbook(in);}else{wb = new XSSFWorkbook(in);}return wb;}static class ExcelVersionUtil{/**** 是否是2003的excel,返回true是2003*** @param filePath* @return* @see [类、类#方法、类#成员]*/public static boolean isExcel2003(String filePath){return filePath.matches("^.+\\.(?i)(xls)$");}/**** 是否是2007的excel,返回true是2007*** @param filePath* @return* @see [类、类#方法、类#成员]*/public static boolean isExcel2007(String filePath){return filePath.matches("^.+\\.(?i)(xlsx)$");}}public static class DateUtil{// ======================日期格式化常量=====================//public static final String YYYY_MM_DDHHMMSS = "yyyy-MM-dd HH:mm:ss";public static final String YYYY_MM_DD = "yyyy-MM-dd";public static final String YYYY_MM = "yyyy-MM";public static final String YYYY = "yyyy";public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";public static final String YYYYMMDD = "yyyyMMdd";public static final String YYYYMM = "yyyyMM";public static final String YYYYMMDDHHMMSS_1 = "yyyy/MM/dd HH:mm:ss";public static final String YYYY_MM_DD_1 = "yyyy/MM/dd";public static final String YYYY_MM_1 = "yyyy/MM";/**** 自定义取值,Date类型转为String类型** @param date 日期* @param pattern 格式化常量* @return* @see [类、类#方法、类#成员]*/public static String dateToStr(Date date, String pattern){SimpleDateFormat format = null;if (null == date)return null;format = new SimpleDateFormat(pattern, Locale.getDefault());return format.format(date);}/*** 将字符串转换成Date类型的时间* <hr>** @param s 日期类型的字符串<br>*            datePattern :YYYY_MM_DD<br>* @return java.util.Date*/public static Date strToDate(String s, String pattern){if (s == null){return null;}Date date = null;SimpleDateFormat sdf = new SimpleDateFormat(pattern);try{date = sdf.parse(s);}catch (ParseException e){e.printStackTrace();}return date;}}}

再提供一个应用实例

	@ApiOperation(value = "以导入excel方式,上传要申请学分的用户")@GetMapping(value = "/uuApplyUserInfo")public AjaxResult uuApplyUserInfo(@RequestParam(value = "files",required = false) MultipartFile files) {try {//工具类ImportExeclUtil readExcelUtil = new ImportExeclUtil();List<List<String>> read = readExcelUtil.read(files.getInputStream(), true);if (CollectionUtils.isNotEmpty(read)){List<ApplyCreditUserDto> importList = read.stream().map(e -> {ApplyCreditUserDto importDto = new ApplyCreditUserDto();importDto.setUserName(e.get(0));importDto.setCreditCardNo(e.get(1));importDto.setCreditCardPwd(e.get(2));return importDto;}).collect(Collectors.toList());if (CollectionUtils.isEmpty(importList)){return AjaxResult.error("不能导入空文件");}//最多导入1W条final int maxInt = 10000;if (importList.size() > maxInt){return AjaxResult.error("导入最多修改1W条");}List<String> orderIds = importList.stream().map(ApplyCreditUserDto::getUserName).distinct().collect(Collectors.toList());if (!Objects.equals(orderIds.size(),importList.size())){return AjaxResult.error("导入信息中,有用户");}//调用业务层return applyCreditLogService.uuApplyUserInfo(importList);}else{return AjaxResult.error("不能导入空文件");}} catch (Exception e) {e.printStackTrace();return AjaxResult.error("导入失败,更新数据库时报错!报错信息:" + e.toString());}}

2、开发中遇到的问题

1)报错"Unable to process parts as no multi-part configuration has been provided "

报错信息截取
“MultipartException: Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: Unable to process parts as no multi-part configuration has been provided
a、如果你在网上搜这个问题,搜到的回答大多是让你在servlet配置中加配置

<multipart-config><max-file-size>20848820</max-file-size><max-request-size>418018841</max-request-size><file-size-threshold>1048576</file-size-threshold>
</multipart-config>

b、但是采用这个答案前,你要看你的项目是否适用上述情况。
SpringMVC处理multipart请求(解析文件请求),有两种实现:CommonsMultipartResolver和StandardServletMultipartResolver。
他们的区别我这里不作详细叙述,就讲一点:
在servlet中配置,是StandardServletMultipartResolver的配置方式;
而CommonsMultipartResolver的配置方式,是在applicationContext中配置

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><property name="defaultEncoding" value="UTF-8" /><property name="maxUploadSize" value="31457280" /><property name="maxInMemorySize" value="4194304" />
</bean>

两种配置的目标基本一致,但是配置位置、对象不一样,搞混了不仅解决不了问题,还会导致其他问题。
c、我这样说,是因为上传文件是基础配置,对于已经运行很久的项目,上传配置前辈们应该早已配置完好,后来人遇到的问题,大多不是缺少“基础”配置引起的,望大家谨慎修改。
d、回到的我的问题,项目采用的是CommonsMultipartResolver解析文件流,我给大家看一下我发现问题的过程:
请求来了先去MultipartFilter过滤器,检测请求是否为包含文件流的请求,打断点可以看到
在这里插入图片描述
我不知道人家业务逻辑是咋处理的,只能step over一步一步往下运行,走到下图这里我大概明白了一点
在这里插入图片描述
这里是判断这个请求是否为“multipart”类型请求,也就是判断是否需要专门解析文件流的部件出手,结果我这儿走到了else里边!
说明检测结论是,我的请求不是multipart请求,或者不符合人家的规范;
于是我重新请求,进入它的判断方法multipartResolver.isMultipart(processedRequest)
里边是

	@Overridepublic boolean isMultipart(HttpServletRequest request) {return (request != null && ServletFileUpload.isMultipartContent(request));}

又一层判断,接着往里边方法看ServletFileUpload.isMultipartContent(request))
在这里插入图片描述
看到这里,我才看明白,人家要求请求必须是Post请求,我尼玛!浪费足足一天时间啊。

之所以没记住是因为“知其然而不知其所以然”,只有知道它的正在原因,才好记住长时间不忘。
其实原因很简单,get请求有大小限制,所以上传功能需要用post请求。
get请求有大小限制这种说法其实也不准确,其实http协议对get请求没有大小、长度限制,限制产生在浏览器和服务器。
服务器一般限制get请求大小在8kb以内;
而浏览器又随型号不通、限制也各不相同,MSIE和Safari的长度限制是2kb,Opear是4kb,Firefox是8KB…
这也解释了,为什么我搜“get请求大小限制是多少”这个问题,回答都各不一样,大家都是“知其然而不知其所以然”、得过且过、断章取义、管中窥豹,我们这种得过且过的态度,就会使本来简单的问题变得越来越复杂,因为它本身已经叠加了太多其他地方的问题。

根据浏览器限制不同,为了保险你可以取限制最小值2kb,然后说“get请求大小限制为2kb”;
又因为一般浏览器限制在4kb,所以又可以说“get请求大小限制为4kb”;
又又因为实际http协议对get请求没有限制,所以有人说“get请求没有大小限制”。。。

嗟乎!希望我们程序员,对自己写的技术类文档,都适当严谨一些,这样才能营造出更好的社区环境。

二、导出

前言

导出excel可以分为两类:
一类是导出excel,里边有我们查询的数据,多用于导出数据;
另一类是导出excel文件,多用于导出模板、而且对模板格式有要求,这时我们提前建一个模板文件,然后存到服务器,导出的时候直接把这个文件传给用户,这种方法其实已经无所谓是什么格式的文件了。

为了方便描述,我们把第一类叫导出excel,第二类叫下载excel模板

1、导出excel

实际运用示例:

    @PostMapping("/export")@ResponseBodypublic void export(HttpServletResponse response, @RequestBody JcWecomTag param){// 设置response的上下文类型response.setContentType("octets/stream");String excelName = "标签数据";try {// 创建一个文件对象HSSFWorkbook workbook = new HSSFWorkbook();// 用文件对象创建一个sheet页,并给sheet页命名HSSFSheet sheet0 = workbook.createSheet("标签");// 用sheet对象创建一个行对象HSSFRow rowm0 = sheet0.createRow(0);// 用行对象创建单元格,给单元格赋值HSSFCell cellColName0 = rowm0.createCell(0);cellColName0.setCellType(CellType.STRING);HSSFRichTextString text0 = new HSSFRichTextString("标签ID");cellColName0.setCellValue(text0);HSSFCell cellColName1 = rowm0.createCell(1);cellColName1.setCellType(CellType.STRING);HSSFRichTextString text1 = new HSSFRichTextString("标签名称");cellColName1.setCellValue(text1);HSSFCell cellColName2 = rowm0.createCell(2);cellColName2.setCellType(CellType.STRING);HSSFRichTextString text2 = new HSSFRichTextString("标签分组ID");cellColName2.setCellValue(text2);HSSFCell cellColName3 = rowm0.createCell(3);cellColName3.setCellType(CellType.STRING);HSSFRichTextString text3 = new HSSFRichTextString("标签分组名称");cellColName3.setCellValue(text3);HSSFCell cellColName4 = rowm0.createCell(4);cellColName4.setCellType(CellType.STRING);HSSFRichTextString text4 = new HSSFRichTextString("bdp字典编码");cellColName4.setCellValue(text4);HSSFCell cellColName5 = rowm0.createCell(5);cellColName5.setCellType(CellType.STRING);HSSFRichTextString text5 = new HSSFRichTextString("bdp字典名称");cellColName5.setCellValue(text5);// 查数据List<JcWecomTag> tagList = wecomTagService.mulitQueryTag(param);// 将查询到的数据设置到sheet对应的单元格中for (int i = 0; i < tagList.size(); i++) {JcWecomTag tag = tagList.get(i);HSSFRow row = sheet0.createRow(i + 1);// 创建所需的行数HSSFCell cell = null;cell = row.createCell(0, CellType.STRING);cell.setCellValue(tag.getTagId());cell = row.createCell(1, CellType.STRING);cell.setCellValue(tag.getTagName());cell = row.createCell(2, CellType.STRING);cell.setCellValue(tag.getTagGroupId());cell = row.createCell(3, CellType.STRING);cell.setCellValue(tag.getTagGroupName());cell = row.createCell(4, CellType.STRING);cell.setCellValue(tag.getDicCode());}// 转码防止乱码response.addHeader("Content-Disposition", "attachment;filename="+ new String(excelName.getBytes("UTF-8"), "ISO8859-1")+ ".xls");OutputStream out = response.getOutputStream();ExcelUtil.writeBySheetAndRow(workbook, out);out.close();} catch (Exception e) {e.printStackTrace();}}

1)示例是SpringMVC项目,Controller接口必须加@ResponseBody注解,否则返回值会被SpringMVC视为视图、会报404错误;

2)最后用到了一个excel工具ExcelUtil.writeBySheetAndRow(workbook, out);

这个工具去网上搜一下就行(估计你们项目本身就有)。导出功能的核心是:配置好Excel工作簿对象HSSFWorkbook ,对象包含标题行、数据行、单元格数据;以及配置好输出流OutputStream;这两个的配置,如上面示例所示配置即可。
这两个对象都弄好之后,剩下的、一般Excel工具类都能处理,不用担心。

2、使用自定义注解-导出excel

1)引入依赖

这里用的是poi导出excel,版本如下

<!-- https://mvnrepository.com/artifact/org.apache.poi/poi --><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.1</version></dependency>

2)自定义注解

为了方便,提高复用效率,这里自定义2个注解;
第一个:@EnableExcel,用来开启Excel表格的导出,用在装导出数据的实体类上;

/***  标记类是否开启Excel* @Author: Sunlong* @date: 2020/5/10 20:29*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableExcel {
}

第二个:@ExcelRow,用在装导出数据的实体类的属性上,用来映射字段与excel的对应关系;

/***  excel 表格 列名注解** @author sunlong*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelRow {/***  Excel 对应列名* @return*/String name();/***  excel 列名备注* @return*/String note() default "";
}

3)代码逻辑—提取导出工具类

a、通过反射获取自定义注解EnableExcel 判断是否开启Excel导出

b、通过反射获取自定义注解ExcelRow 获取列对应的属性

c、把属性对应的列下标取出来,属性名做为key,下标做为value放到map中

d、遍历要导出的数据集合,通过属性描述器PropertyDescriptor获取对应属性下标及属性值并设置到cell单元格中

还是为了方便,已经提高复用效率,我们将上述代码提取成一个工具类,如下:

public class ExportExcelUtils {/***  workbook* @param titleList* @return*/public static HSSFWorkbook getWorkBook(List<String> titleList){//第一步,创建一个workbook,对应一个Excel文件HSSFWorkbook wb = new HSSFWorkbook();// 一个sheetHSSFSheet sheet = wb.createSheet("sheet1");HSSFRow rowTitle = sheet.createRow(0); // 第一行 标题// 设置标题for (int i = 0; i < titleList.size(); i++) {HSSFCell cell = rowTitle.createCell(i);cell.setCellValue(titleList.get(i));}//合并单元格CellRangeAddress构造参数依次表示起始行,截至行,起始列, 截至列/*sheet.addMergedRegion(new CellRangeAddress(0,0,0,4));sheet.addMergedRegion(new CellRangeAddress(titleList.size()-1,titleList.size()-1,titleList.size()-1,titleList.size()+1));*/return wb;}public static <T> HSSFWorkbook getWorkBook(List<String> titleList , List<T> dataList) throws IntrospectionException, InvocationTargetException, IllegalAccessException {if (CollectionUtils.isNotEmpty(dataList)) {T t1 = dataList.get(0);Class<?> t1Class = t1.getClass();EnableExcel enableExcel = t1Class.getAnnotation(EnableExcel.class);if (enableExcel == null) {throw new IllegalArgumentException("EnableExcel 注解没有在实体类启用");}Field[] fields = t1Class.getDeclaredFields();if (fields != null && fields.length > 0) {Map<String , Integer> titleMap = new HashMap<>(titleList.size()); // 存放属性名称对应的下标int fieldExcelSize = 0; // 类中ExcelRow 注解的数量for (Field field : fields) {field.setAccessible(true);String fieldName = field.getName();ExcelRow excelRow = field.getAnnotation(ExcelRow.class);if (excelRow != null) {String name = excelRow.name();if (StringUtils.isEmpty(name)) {throw new IllegalArgumentException("ExcelRow 注解name属性不能为空");}int index = titleList.indexOf(name.trim());if (index != -1) {fieldExcelSize++;titleMap.put(fieldName , index);}}}if (!(titleList.size() == fieldExcelSize)) {throw new IllegalArgumentException("ExcelRow 注解name属性对应的列数不对");}HSSFWorkbook workBook = getWorkBook(titleList);HSSFSheet sheet = workBook.getSheetAt(0);for (T t : dataList) {int lastRowNum = sheet.getLastRowNum();HSSFRow row = sheet.createRow(lastRowNum + 1);BeanInfo beanInfo = Introspector.getBeanInfo(t.getClass());PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {String fieldName = propertyDescriptor.getName();if (titleMap.containsKey(fieldName)) {Method readMethod = propertyDescriptor.getReadMethod();if (readMethod != null) {Class<?> returnType = readMethod.getReturnType();String simpleName = returnType.getSimpleName();Object invoke = readMethod.invoke(t);String value = "";// 可以根据不同的类型返回不同的数据if ("date".equalsIgnoreCase(simpleName)) {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");if (invoke != null) {value = simpleDateFormat.format(invoke);}}if (invoke != null && "".equals(value)) {value = invoke.toString();}row.createCell(titleMap.get(fieldName)).setCellValue(value);}}}}return workBook;}}return null;}
}

4)应用实例

创建一个用来装导出数据的类(为了不与项目其他功能冲突,我一般都是新建一个专门做导出的实体类)

@EnableExcel
@Data
public class UserEntity {@ExcelRow(name = "name")private String username;@ExcelRow(name = "pass")private String password;@ExcelRow(name = "date")private Date createDate;}

模拟导出功能:

public class Test {public static void main(String[] args) throws IllegalAccessException, IntrospectionException, InvocationTargetException, IOException {List<String> titleList = new ArrayList<>();titleList.add("name");titleList.add("pass");titleList.add("date");List<UserEntity> userEntities = new ArrayList<>();for (int i = 0; i < 10; i++) {UserEntity userEntity1 = new UserEntity();userEntity1.setUsername("username"+i);userEntity1.setPassword("password"+i);userEntity1.setCreateDate(new Date());userEntities.add(userEntity1);}HSSFWorkbook workBook = ExportExcelUtils.getWorkBook(titleList, userEntities);if (workBook != null) {File file = new File("D:\\test_export.xlsx");workBook.write(file);}}
}

3、使用easyPOI–导出excel

1)easyPOI简介

easyPOI是一个以Apache poi为基础的工具包、一款开源框架,用于实现excel,word,pdf的导入导出;开发者为Lemur。

特性
基于注解的导入导出,修改注解就可以修改Excel
支持常用的样式自定义
基于map可以灵活定义的表头字段
支持一对多的导入、导出
支持模板的导出,一些常见的标签,自定义标签
支持HTML/Excel转换
支持word的导出,支持图片,Excel

2)导出Excel使用实例

a)添加依赖

		<dependency><groupId>cn.easyproject</groupId><artifactId>orai18n</artifactId></dependency>

需要注意的是由于easypoi的依赖内部依赖原生的poi,所以,引入了easypoi的依赖之后,需要把原生的poi的依赖删掉

b)需要引入一个easyPoi导入导出工具类

/*** 描述:easyPoi导入导出类* @author 梅西库里RNG*/
@Slf4j
public class EasyPoiExcelUtil {public static String dictColIndex="colIndex";public static String dictColValue="colDicts";/*** 常用普通导出* @param list* @param sheetName* @param pojoClass* @return*/public static Workbook exportExcel(List<?> list, String sheetName,Class<?> pojoClass) {return exportExcel(list,sheetName,pojoClass,null);}/*** 常用普通导出 带字典* @param list* @param sheetName* @param pojoClass* @param dicts* @return*/public static Workbook exportExcel(List<?> list, String sheetName,Class<?> pojoClass,List<Map<String,Object>> dicts) {return exportExcel(list,null,sheetName,pojoClass,true,dicts);}/*** 常用普通导入* @auther zhangdongsheng* @param file* @param pojoClass* @param <T>* @return*/public static <T> List<T> importExcel(MultipartFile file,Class<T> pojoClass) {return importExcel(file,0,1,pojoClass);}/*** 得到Workbook对象** @param file* @return* @throws IOException*/private static Workbook getWorkBook(MultipartFile file) throws IOException {//这样写  excel 能兼容03和07InputStream is = file.getInputStream();Workbook hssfWorkbook = null;try {hssfWorkbook = new HSSFWorkbook(is);} catch (Exception ex) {is = file.getInputStream();hssfWorkbook = new XSSFWorkbook(is);}return hssfWorkbook;}/*** 得到错误信息** @param sb* @param list* @param i* @param obj* @param name 用哪个属性名去表明不和规定的数据* @param msg* @throws Exception*/private static void getWrongInfo(StringBuilder sb, List list, int i, Object obj, String name, String msg) {Class clazz = obj.getClass();Object str = null;//得到属性名数组Field[] fields = clazz.getDeclaredFields();try{for (Field f : fields) {if (f.getName().equals(name)) {//用来得到属性的get和set方法PropertyDescriptor pd = new PropertyDescriptor(f.getName(), clazz);//得到get方法Method getMethod = pd.getReadMethod();str = getMethod.invoke(obj);}}if (i == 0) {sb.append(msg + str + ";");} else if (i == (list.size() - 1)) {sb.append(str + "</br>");} else {sb.append(str + ";");}}catch (Exception e){log.error("程序异常:{}",e);}}public static Workbook exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, boolean isCreateHeader,List<Map<String,Object>> dicts) {ExportParams exportParams = new ExportParams();exportParams.setSheetName(sheetName);if (title!=null){exportParams.setTitle(sheetName);}exportParams.setCreateHeadRows(isCreateHeader);return defaultExport(list, pojoClass, exportParams,dicts);}public static Workbook exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass) {return defaultExport(list, pojoClass, new ExportParams(title, sheetName,ExcelType.XSSF));}public static Workbook exportExcel(List<Map<String, Object>> list) {return defaultExport(list);}private static Workbook defaultExport(List<?> list, Class<?> pojoClass, ExportParams exportParams) {return ExcelExportUtil.exportExcel(exportParams, pojoClass, list);}private static Workbook defaultExport(List<?> list, Class<?> pojoClass, ExportParams exportParams,List<Map<String,Object>> dicts) {int rowCnt=list!=null&&list.size()>0?list.size():0;Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);if(dicts!=null&&dicts.size()>0){if(workbook!=null&&workbook.getNumberOfSheets()>0){Sheet sheet =workbook.getSheetAt(0);if(rowCnt>0){int curRow=1;//一般会带头部 从第1行开始for (int i = 0; i < rowCnt; i++) {int firstRow=i+curRow;for(Map<String,Object> map:dicts) {int firstCol=(int)map.get(dictColIndex);String[] dict=(String[])map.get(dictColValue);CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(firstRow, firstRow, firstCol, firstCol);DVConstraint dvConstraint = DVConstraint.createExplicitListConstraint(dict);HSSFDataValidation dataValidation = new HSSFDataValidation(cellRangeAddressList, dvConstraint);sheet.addValidationData(dataValidation);}}}}}return workbook;}private static Workbook defaultExport(List<Map<String, Object>> list) {return ExcelExportUtil.exportExcel(list, ExcelType.XSSF);}private static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) {if (StringUtils.isBlank(filePath)) {return new ArrayList<>();}ImportParams params = new ImportParams();params.setTitleRows(titleRows);params.setHeadRows(headerRows);List<T> list = null;try {list = ExcelImportUtil.importExcel(new File(filePath), pojoClass, params);}catch (Exception e) {log.error("程序异常",e);}return list;}private static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) {if (file == null) {return new ArrayList<>();}ImportParams params = new ImportParams();params.setTitleRows(titleRows);params.setHeadRows(headerRows);List<T> list = null;try {list = ExcelImportUtil.importExcel(file.getInputStream(), pojoClass, params);}  catch (Exception e) {log.error("程序异常",e);}return list;}/*** 根据接收的Excel文件来导入Excel,并封装成实体类** @param file       上传的文件* @param titleRows  表标题的行数* @param headerRows 表头行数* @param pojoClass  Excel实体类*/private static <T> ExcelImportResult<T> importExcelVerify(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) {if (ObjectUtil.isNull(file)) {return null;}ImportParams params = new ImportParams();params.setTitleRows(titleRows);params.setHeadRows(headerRows);//是否开启校验params.setNeedVerify(true);List<T> list = null;try {return ExcelImportUtil.importExcelMore(file.getInputStream(), pojoClass, params);} catch (Exception e) {log.error(">>> 导入数据异常:{}", e.getMessage());return null;}}
}

c)Controller方法

	@ApiOperation("列表 导出")@PostMapping (value="/search/export")public void exportOrgList(HttpServletResponse response, @RequestBody CustomerVo customerVo) throws Exception {//查询List<CustomerAdminVo> list = customerService.searchAdmin(customerVo);//调用工具,生成workbook工作簿对象Workbook workbook=HysExcelUtil.exportExcel(list,"行政用户-列表",CustomerAdminVo.class);//配置response对象response.setContentType("application/x-excel");response.setCharacterEncoding("UTF-8");response.setHeader("Content-Disposition","attachment; filename=customer_admin_export.xls");//生成输出流对象,并把工作薄写入到输出流OutputStream oStream = response.getOutputStream();// 输出流workbook.write(oStream);//把工作薄写入到输出流//关闭流try {oStream.close();} catch (IOException e) {e.printStackTrace();}}

上面代码中的

HysExcelUtil.exportExcel(list,"行政用户-列表",CustomerAdminVo.class);

第三个参数CustomerAdminVo.class,是用来设定导出excel的列名、列顺序、与数据的映射关系、列的宽度、日期格式等;最好给每一个导出模板,配一个独立的vo类,用以个性化配置。

@ApiModel(value = "CustomerAdminVo", description = "用户")
@Data
public class CustomerAdminVo implements Serializable {private static final long serialVersionUID = -3477299713883180124L;@Excel(name = "账户名称", orderNum = "0", width = 30)@ApiModelProperty(value = "账户名称 不为空在账号表有记录",dataType ="String",required = true)private String accountName;@Excel(name = "真实姓名", orderNum = "1", width = 30)@ApiModelProperty(value = "真实姓名")private String realName;@Excel(name = "手机号码", orderNum = "2", width = 30)@ApiModelProperty(name = "mobilNumber",value = "手机号码",dataType ="String")private String mobilNumber;@Excel(name = "添加时间", orderNum = "14", expertFormat =  "yyyy-MM-dd HH:mm:ss")private Date createTime;
}

其中,用于设置上述内容的@Excel注解,就是easyPOI提供的注解

import cn.afterturn.easypoi.excel.annotation.Excel;

注解的属性中,
name 列名,orderNum 顺序,width 列宽,expertFormat 时间字段导出格式(不为空时,按指定格式格式化时间)

d)导出结果

在这里插入图片描述

4、导出excel模板(其实导出word、pdf都可以,就是导出文件的方法)

1)首先,文件要提前放在对应的位置,如下图:
在这里插入图片描述
2)导出的controller方法示例:

@RequestMapping("/downloadTemp")
public void downloadFile(HttpServletRequest request, HttpServletResponse response) throws Exception {String fileName = "org_import.xls"; // 下载的文件名(前提:下载的文件需要存放在服务器对应的位置上)response.setContentType("text/html;charset=UTF-8");BufferedInputStream in = null;BufferedOutputStream out = null;request.setCharacterEncoding("UTF-8");String rootpath = request.getSession().getServletContext().getRealPath("/");try {File f = new File(rootpath + "res/" + fileName);//这里文件名,要和文件放的位置对应response.setContentType("application/x-excel");response.setCharacterEncoding("UTF-8");response.setHeader("Content-Disposition","attachment; filename="+ new String("机构导入.xls".getBytes("gbk"),"iso-8859-1"));response.setHeader("Content-Length", String.valueOf(f.length()));in = new BufferedInputStream(new FileInputStream(f));out = new BufferedOutputStream(response.getOutputStream());byte[] data = new byte[1024];int len = 0;while (-1 != (len = in.read(data, 0, data.length))) {out.write(data, 0, len);}} catch (Exception e) {out.write(e.toString().getBytes());e.printStackTrace();} finally {if (in != null) {in.close();}if (out != null) {out.close();}}}

http://chatgpt.dhexx.cn/article/22vfXFqf.shtml

相关文章

Java实现Excel导入

实现前准备&#xff08;导入所需要的依赖&#xff09; <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.22</version></dependency><dependency><groupId>org.apache.…

导入Excel文件的方法

我们在做一些项目的时候通常会遇到有导入文件的这种需求&#xff0c;下面我给大家分享一下导入Excel文件的方法。 首先我们得准备一个模板&#xff0c;下图就是一个简单的EX导入模板&#xff0c; 下图就是一个导入模板&#xff0c; 我们先把导入模板写好&#xff0c; 还有一个…

两种方式导入excel

第一种 easyExcel pom文件导入 com.alibaba easyexcel 2.2.3 然后 /** * 导入用户excel * param * return */ PostMapping(“manage/imporAcc”) Message<?> imporAcc(RequestParam(“accountFile”) MultipartFile file,RequestParam(“role”)String role,Request…

Java实现Excel导入和导出,看这一篇就够了(珍藏版)

目录 目录 前言 1. 功能测试 1.1 测试准备 1.2 数据导入 1.2.1 导入解析为JSON 1.2.2 导入解析为对象&#xff08;基础&#xff09; 1.2.3 导入解析为对象&#xff08;字段自动映射&#xff09; 1.2.4 导入解析为对象&#xff08;获取行号&#xff09; 1.2.5 导入解析…

常见机器学习面试题

参考&#xff1a;http://kubicode.me/2015/08/16/Machine%20Learning/Common-Interview/?fromsinglemessage# http://blog.csdn.NET/heyongluoyao8/article/details/49429629 http://lib.csdn.Net/article/machinelearning/33798 http://www.cnblogs.com/zuochongyan/p/540705…

机器学习面试题60~100

61.说说梯度下降法 LeftNotEasy&#xff0c;本题解析来源&#xff1a;http://www.cnblogs.com/LeftNotEasy/archive/2010/12/05/mathmatic_in_machine_learning_1_regression_and_gradient_descent.html 下面是一个典型的机器学习的过程&#xff0c;首先给出一个输入数据&#…

深度学习机器学习面试题汇——模型优化,轻量化,模型压缩

深度学习机器学习面试题汇——模型优化&#xff0c;轻量化&#xff0c;模型压缩 提示&#xff1a;互联网大厂可能考的面试题 若CNN网络很庞大&#xff0c;在手机上运行效率不高&#xff0c;对应模型压缩方法有了解吗 介绍一下模型压缩常用的方法&#xff1f;为什么用知识蒸馏&…

Python干货:破解40大机器学习面试题(包含初中高级)

机器学习&#xff08;ML&#xff09;是我们世界的未来。在未来的几年中&#xff0c;几乎每种产品都将包含ML组件。ML预计将从2020年的$ 7.3B增长到2024年的$ 30.6B。对ML技能的需求遍及整个行业。 机器学习面试是一个严格的过程&#xff0c;在此过程中&#xff0c;应聘者会评估…

2021机器学习面试必考面试题汇总(附答案详解)

问题&#xff1a;Xgboost、lightGBM和Catboost之间的异同&#xff1f; 树的特征 三种算法基学习器都是决策树&#xff0c;但是树的特征以及生成的过程仍然有很多不同。 CatBoost使用对称树&#xff0c;其节点可以是镜像的。CatBoost基于的树模型其实都是完全二叉树。 XGBoo…

机器学习面试题之——简单介绍最小二乘

1、常用到的最小二乘场合&#xff1a;最小二乘法直线拟合&#xff0c;最小二乘法多项式&#xff08;曲线&#xff09;拟合&#xff0c;机器学习中线性回归的最小二乘法&#xff0c;系统辨识中的最小二乘辨识法&#xff0c;参数估计中的最小二乘法&#xff0c;等等。 2、为什么…

AI人工智能、机器学习 面试题(2022最新版)

人工智能、机器学习面试题总结&#xff0c;侧重于理解&#xff0c;回答供参考&#xff0c;欢迎讨论。 General 深度学习&#xff08;Deep Learning, DL&#xff09;和机器学习&#xff08;Machine Learning, ML&#xff09;的关系是什么&#xff1f; 深度学习是机器学习的子类…

面试官最爱用的统计学、数据科学、机器学习面试题答案

【导读】本文盘点了数据科学和机器学习面试中的常见问题&#xff0c;着眼于不同类型的面试问题。如果您计划向数据科学领域转行&#xff0c;这些问题一定会有所帮助。 技术的不断进步使得数据和信息的产生速度今非昔比&#xff0c;并且呈现出继续增长的趋势。此外&#xff0c;…

41个机器学习面试题

####41 Essential Machine Learning Interview Questions (with answers) <font color‘orange’&#xff0c;size5>一、算法理论 Q1: 什么是偏倚&#xff08;bias&#xff09;、方差&#xff08;variable&#xff09;均衡&#xff1f; 偏倚指的是模型预测值与真实值的…

推荐收藏,25道机器学习面试问题(附答案)

近年来&#xff0c;对深度学习的需求不断增长&#xff0c;其应用程序被应用于各个商业部门。各公司现在都在寻找能够利用深度学习和机器学习技术的专业人士。 在本文中&#xff0c;将整理深度学习面试中最常被问到的25个问题和答案。如果你最近正在参加深度学习相关的面试工作…

机器学习面试题之机器学习基础(一)

1、L1与L2正则化 他们都是可以防止过拟合&#xff0c;降低模型复杂度。 L1会趋向于产生少量的特征&#xff0c;而其他的特征都是0&#xff1b;L2会选择更多的特征&#xff0c;这些特征都会接近于0。L1在特征选择时非常有用&#xff0c;L2就只是一种规则化而已。 简单总结一下就…

机器学习面试必考面试题汇总—附解析

问题&#xff1a;xgboost对特征缺失敏感吗&#xff0c;对缺失值做了什么操作&#xff0c;存在什么问题 不敏感&#xff0c;可以自动处理&#xff0c;处理方式是将missing值分别加入左节点 右节点取分裂增益最大的节点将missing样本分裂进这个节点 。这种处理方式的问题在xgboo…

机器学习面试题——聚类算法

机器学习面试题——聚类算法 提示&#xff1a;互联网大厂经常考的传统机器学习算法 文章目录 机器学习面试题——聚类算法[TOC](文章目录) 题目k-means介绍一下&#xff0c;K-means的过程k-means优缺点k-means的簇&#xff08;k簇&#xff09;怎么选&#xff0c;K-means如何选取…

机器学习面试题目整理

0 调参技巧 清洗数据&#xff0c;数据预处理&#xff0c;数据增广是否使用预训练模型使用BN在过拟合后&#xff0c;使用正则化技巧如L1、L2、Dropout对于不均匀样本&#xff0c;使用重采样使用合适的优化器。第一&#xff0c;如果你关心快速收敛&#xff0c;使用自适应优化器&…

面试 | 22道机器学习常见面试题目

(1) 无监督和有监督算法的区别&#xff1f; 有监督学习&#xff1a;对具有概念标记&#xff08;分类&#xff09;的训练样本进行学习&#xff0c;以尽可能对训练样本集外的数据进行标记&#xff08;分类&#xff09;预测。这里&#xff0c;所有的标记&#xff08;分类&#xf…

最基本的25道深度学习面试问题和答案

近年来&#xff0c;对深度学习的需求不断增长&#xff0c;其应用程序被应用于各个商业部门。各公司现在都在寻找能够利用深度学习和机器学习技术的专业人士。在本文中&#xff0c;将整理深度学习面试中最常被问到的25个问题和答案。如果你最近正在参加深度学习相关的面试工作&a…