Java实现Excel导入导出的步骤详解

作者:梅西库里RNG 时间:2022-09-05 05:20:32 

一、导入

前言:导入必须用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: // Boolean
                           cellValue = 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: // Boolean
                   cellValue = 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 "

报错信息截取
&ldquo;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&rdquo;
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、我这样说,是因为上传文件是基础配置,对于已经运行很久的项目,上传配置前辈们应该早已配置完好,后来人遇到的问题,大多不是缺少&ldquo;基础&rdquo;配置引起的,望大家谨慎修改。
d、回到的我的问题,项目采用的是CommonsMultipartResolver解析文件流,我给大家看一下我发现问题的过程:
请求来了先去MultipartFilter过滤器,检测请求是否为包含文件流的请求,打断点可以看到

Java实现Excel导入导出的步骤详解

我不知道人家业务逻辑是咋处理的,只能step over一步一步往下运行,走到下图这里我大概明白了一点

Java实现Excel导入导出的步骤详解

这里是判断这个请求是否为&ldquo;multipart&rdquo;类型请求,也就是判断是否需要专门解析文件流的部件出手,结果我这儿走到了else里边!
说明检测结论是,我的请求不是multipart请求,或者不符合人家的规范;
于是我重新请求,进入它的判断方法multipartResolver.isMultipart(processedRequest);
里边是

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

又一层判断,接着往里边方法看ServletFileUpload.isMultipartContent(request))

Java实现Excel导入导出的步骤详解

看到这里,我才看明白,人家要求请求必须是Post请求,我尼玛!浪费足足一天时间啊。

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

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

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

二、导出

前言

导出excel可以分为两类:

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

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

1、导出excel

实际运用示例:

@PostMapping("/export")
   @ResponseBody
   public 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)代码逻辑&mdash;提取导出工具类

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();
       // 一个sheet
       HSSFSheet 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&ndash;导出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和07
       InputStream 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)导出结果

Java实现Excel导入导出的步骤详解

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

1)首先,文件要提前放在对应的位置,如下图:

Java实现Excel导入导出的步骤详解

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();
           }
       }
   }

来源:https://blog.csdn.net/Derek7117/article/details/127287322

标签:Java,Excel,导入,导出
0
投稿

猜你喜欢

  • 详解java8在Collection中新增加的方法removeIf

    2022-06-04 20:51:45
  • java生成jar包并且单进程运行的实例

    2021-08-30 00:40:55
  • Java @Transactional指定回滚条件

    2023-11-19 02:47:23
  • Java class文件格式之常量池_动力节点Java学院整理

    2023-04-14 07:14:06
  • SpringBoot自定义注解实现Token校验的方法

    2023-11-13 23:17:52
  • 微服务通过Feign调用进行密码安全认证操作

    2023-07-30 02:43:38
  • java 避免出现NullPointerException(空指针)的方法总结

    2022-08-31 04:15:51
  • SpringBoot使用Mybatis&Mybatis-plus文件映射配置方法

    2023-05-16 12:53:02
  • Java中JMM与volatile关键字的学习

    2022-03-24 00:12:00
  • Springboot 异步任务和定时任务的异步处理

    2022-08-20 07:21:12
  • Java基于解释器模式实现定义一种简单的语言功能示例

    2021-06-18 15:53:42
  • java 生成xml并转为字符串的方法

    2023-01-07 08:27:30
  • Java集合的Collection接口和List接口详解

    2021-11-02 05:24:44
  • Java基于对象流实现银行系统

    2023-09-10 02:54:21
  • java Mail邮件接收工具类

    2022-04-24 14:17:17
  • 实例讲解Java读取一般文本文件和word文档的方法

    2023-11-13 05:09:53
  • SpringMVC的执行过程浅析

    2021-05-31 20:51:11
  • Java基于TCP方式的二进制文件传输

    2023-06-04 18:23:52
  • hibernate 中 fetch=FetchType.LAZY 懒加载失败处理方法

    2023-11-08 09:40:31
  • SpringBoot Security前后端分离登录验证的实现

    2023-03-09 10:30:07
  • asp之家 软件编程 m.aspxhome.com