# 操作Excel的3种方式 **Repository Path**: fpfgitmy_admin/three-ways-to-operate-excel ## Basic Information - **Project Name**: 操作Excel的3种方式 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-04-28 - **Last Updated**: 2021-04-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### 操作Excel的3种方式 + 相同点: 1. Easypoi 和 Easyexcel 都是基于Apache poi进行二次开发的 + 不同点: 1. Easypoi 在读写数据的时候,优先是先将数据写入内存,优点是读写性能非常高,但是当数据量很大的时候,会出现oom,当然它也提供了 sax 模式的读写方式,需要调用特定的方法实现 2. Easyexcel 基于sax模式进行读写数据,不会出现oom情况,程序有过高并发场景的验证,因此程序运行比较稳定,相对于 Easypoi 来说,读写性能稍慢 #### Apache poi ##### 基础model ``` @Data public class Bsheet { /** * sheet的名称 */ private String sheetName = Constants.SHEET_NAME; /** * 文件名称 */ private String fileName = LocalDateTime.now().getMinute() + ""; /** * 是否需要合并第一行 */ private Boolean isMerge = false; /** * 合并的第一行的名称 */ private String firstMergeName = Constants.FIRST_MERGE_NAME; /** * 合并第一行的截至列 */ private int endCell; /** * 表格的头部 */ private String[] header; /** * 表格数据 */ private List data; } ``` ##### 相关操作代码 ###### 添加依赖 ``` org.apache.poi poi 4.1.2 org.apache.poi poi-ooxml 4.1.2 joda-time joda-time 2.10.6 ``` ###### 编写util ``` public class ExcelUtil { // HSSFWorkbook导出 .xls方式 private Workbook wb = null; // XSSF方式导出.xlsx方式 private XSSFWorkbook xwb = null; // SXSSF方式导出.xlsx方式 private SXSSFWorkbook swb = null; private Sheet sheet = null; private Row row = null; private Cell cell = null; // 样式的设置 private CellStyle style = null; private Font font = null; private InputStream inputStream = null; // excel的格式 默认xlsx public static String type = Constants.EXCEL_TYPE_XLSX; public ExcelUtil(Bsheet bsheet) { switch (type) { case Constants.EXCEL_TYPE_XLS: // 最多只支持65536条数据导出,超过这个条数会报错 wb = new HSSFWorkbook(); sheet = wb.createSheet(); style = wb.createCellStyle(); font = wb.createFont(); break; case Constants.EXCEL_TYPE_XLSX: // XSSF方式支持大批量数据导出,所有的数据先写入内存再导出,容易出现内存溢出 xwb = new XSSFWorkbook(); sheet = xwb.createSheet(); style = xwb.createCellStyle(); font = xwb.createFont(); break; case Constants.EXCEL_TYPE_SXLSX: // XSSF方式支持大批量数据导出,所有的数据先写入内存再导出,容易出现内存溢出 swb = new SXSSFWorkbook(); sheet = swb.createSheet(); style = swb.createCellStyle(); font = swb.createFont(); break; default: xwb = new XSSFWorkbook(); sheet = xwb.createSheet(); style = xwb.createCellStyle(); font = xwb.createFont(); } } /*-----------------------------------------------------------导出excel-----------------------------------------------------------*/ /** * 创建Excel * * @param request * @param response * @param bsheet */ public void createExcel(HttpServletRequest request, HttpServletResponse response, Bsheet bsheet) { String fileName = getFileName(request, bsheet.getFileName()); // 是否合并第一行 if (bsheet.getIsMerge()) { row = sheet.createRow(0); cell = row.createCell(0); if (StringUtils.isNotBlank(bsheet.getFirstMergeName())) { cell.setCellValue(bsheet.getFirstMergeName()); } // 起始行,截至行,起始列, 截至列 sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, bsheet.getEndCell())); } // 设置头数据 setHeader(bsheet.getHeader(), bsheet.getIsMerge()); // 设置表数据(默认设置header) addRow(bsheet.getData(), bsheet.getEndCell()); try { response.reset(); response.setContentType("application/x-msdownload"); response.setHeader("Content-Disposition", "attachment; filename=" + fileName); ServletOutputStream sos = response.getOutputStream(); switch (type) { case Constants.EXCEL_TYPE_XLS: wb.write(sos); break; case Constants.EXCEL_TYPE_XLSX: xwb.write(sos); break; case Constants.EXCEL_TYPE_SXLSX: swb.write(sos); break; default: xwb.write(sos); } sos.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 获取文件名称 * * @param request * @param fileName * @return */ private String getFileName(HttpServletRequest request, String fileName) { fileName = fileName.replace(" ", ""); String userAgent = request.getHeader("user-agent").toLowerCase(); if (StringUtils.isBlank(fileName)) { fileName = System.currentTimeMillis() + ""; } fileName = fileName + (type.equals("xls") ? ".xls" : ".xlsx"); if (userAgent.contains("msie") || userAgent.contains("like gecko")) { try { fileName = URLEncoder.encode(fileName, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } else { try { fileName = new String(fileName.getBytes("UTF-8"), "iso-8859-1"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } return fileName; } /** * 设置头部数据 * * @param headers * @param isMerge */ private void setHeader(String[] headers, Boolean isMerge) { row = sheet.createRow(0); // 获取样式 CellStyle style = getStyle(); if (isMerge) { row = sheet.createRow(1); } for (int i = 0; i < headers.length; i++) { cell = row.createCell(i); cell.setCellValue(headers[i]); cell.setCellStyle(style); } } /** * 添加数据 * * @param dataList * @param endCell */ private void addRow(List dataList, int endCell) { for (int i = 0; i < dataList.size(); i++) { row = sheet.createRow(i + 1); for (int j = 0; j < endCell; j++) { cell = row.createCell(j); cell.setCellValue(dataList.get(i)[j]); } } } /** * 获取行样式 * * @return */ private CellStyle getStyle() { // 设置样式 style.setAlignment(HorizontalAlignment.CENTER); // 居中 // 自动换行 style.setWrapText(true); // 设置字体 font.setFontName("宋体"); font.setFontHeightInPoints((short) 12);//设置字体大小 style.setFont(font); return style; } /*-----------------------------------------------------------读取excel-----------------------------------------------------------*/ public ExcelUtil(InputStream inputStream) { this.inputStream = inputStream; // 实例化操作 initWorkBook(); } /** * 获取workBook实例 */ public void initWorkBook() { try { switch (type) { // HSSF case Constants.EXCEL_TYPE_XLS: wb = new HSSFWorkbook(inputStream); sheet = wb.getSheetAt(0); break; case Constants.EXCEL_TYPE_XLSX: // XSSF wb = new XSSFWorkbook(inputStream); sheet = wb.getSheetAt(0); break; case Constants.EXCEL_TYPE_SXLSX: wb = new SXSSFWorkbook(); sheet = wb.getSheetAt(0); break; default: wb = new XSSFWorkbook(inputStream); sheet = wb.getSheetAt(0); } } catch (IOException e) { e.printStackTrace(); } } /** * 根据流读取excel数据 */ public String readExcel(int rowNum, int cellNum) { if (null == inputStream) { throw new RuntimeException("inputStream is not null"); } row = sheet.getRow(rowNum); cell = row.getCell(cellNum); String value = getValue(cell); try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } return value; } /** * 获取总行数 * * @return */ public int readRowCountNum() { return sheet.getLastRowNum(); } /** * 获取总列数 * * @param rowNum * @return */ public int readCellCount(int rowNum) { row = sheet.getRow(rowNum); return row.getLastCellNum(); } /** * 获取单元格的值 * * @param cell * @return */ public static String getValue(Cell cell) { String cellValue = ""; //匹配类型数据 if (cell != null) { CellType cellType = cell.getCellType(); switch (cellType) { case STRING: //字符串 cellValue = cell.getStringCellValue(); break; case BOOLEAN: //布尔类型 cellValue = String.valueOf(cell.getBooleanCellValue()); break; case BLANK: //空 break; case NUMERIC: //数字(日期、普通数字) if (HSSFDateUtil.isCellDateFormatted(cell)) { //日期 System.out.print("[日期]"); Date date = cell.getDateCellValue(); cellValue = new DateTime(date).toString("yyyy-MM-dd"); } else { //不是日期格式,防止数字过长 cell.setCellType(CellType.STRING); cellValue = cell.toString(); } break; case ERROR: break; default: } } return cellValue; } } ``` ###### 编写测试方法 ``` /** * 导出excel * * @param fileName * @param request * @param response */ @GetMapping("excel/export") public void exportTest( @RequestParam String fileName, HttpServletRequest request, HttpServletResponse response ) { String[] headers = {"姓名", "年龄", "手机号", "余额"}; ArrayList dataList = new ArrayList<>(); for (int i = 0; i < 10; i++) { dataList.add(new String[]{ "f1" + i, i + 1 + "", "1566495158" + i, "1585869" + i }); } Bsheet bsheet = new Bsheet(); bsheet.setFileName(fileName); bsheet.setHeader(headers); bsheet.setEndCell(headers.length); bsheet.setData(dataList); // 设置导出方式 ExcelUtil.type = Constants.EXCEL_TYPE_XLSX; ExcelUtil excelUtil = new ExcelUtil(bsheet); excelUtil.createExcel(request, response, bsheet); } /** * 以路径方式读取 * * @throws FileNotFoundException */ @GetMapping(value = "excel/read") public void readExcelForPathTest() throws FileNotFoundException { // 设置读取方式 ExcelUtil.type = Constants.EXCEL_TYPE_XLSX; String path = "D:\\file\\excel\\22222.xlsx"; FileInputStream fileInputStream = new FileInputStream(path); ExcelUtil excelUtil = new ExcelUtil(fileInputStream); int row = excelUtil.readRowCountNum(); int cell = excelUtil.readCellCount(row); for (int j = 0; j < row; j++) { for (int k = 0; k < cell; k++) { String s = excelUtil.readExcel(j, k); System.out.println("读取到第" + (j + 1) + "行," + (k + 1) + "列的值=" + s); } } } /** * 以文件方式读取 * * @param file * @throws IOException */ @PostMapping(value = "excel/read", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public void readExcelForStreamTest(@RequestParam("file") MultipartFile file) throws IOException { InputStream inputStream = file.getInputStream(); String filename = file.getOriginalFilename(); // 设置读取方式 if (filename.contains(".xlsx")) { ExcelUtil.type = Constants.EXCEL_TYPE_XLSX; } else if (filename.contains(".xls")) { ExcelUtil.type = Constants.EXCEL_TYPE_XLS; } else { ExcelUtil.type = Constants.EXCEL_TYPE_XLSX; } ExcelUtil excelUtil = new ExcelUtil(inputStream); int row = excelUtil.readRowCountNum(); int cell = excelUtil.readCellCount(row); for (int j = 0; j < row; j++) { for (int k = 0; k < cell; k++) { String s = excelUtil.readExcel(j, k); System.out.println("读取到第" + (j + 1) + "行," + (k + 1) + "列的值=" + s); } } } ``` #### EasyPoi + 简介: easypoi底层也是基于 apache poi 进行深度开发的,它主要的特点就是将更多重复的工作,全部简单化,避免编写重复的代码! ##### 添加依赖 ``` cn.afterturn easypoi-base 4.1.0 cn.afterturn easypoi-web 4.1.0 cn.afterturn easypoi-annotation 4.1.0 com.googlecode.json-simple json-simple 1.1.1 ``` ##### 相关操作代码 ###### 基础model ``` public class UserExcel { @Excel(name = "昵称") private String nickName; @Excel(name = "真实姓名") private String realName; @Excel(name = "头像") private String avatar; @Excel(name = "性别") private Integer sex; @Excel(name = "类型") private Integer type; @Excel(name = "手机号") private String phone; @Excel(name = "创建时间") private Date createTime; public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } public String getRealName() { return realName; } public void setRealName(String realName) { this.realName = realName; } public String getAvatar() { return avatar; } public void setAvatar(String avatar) { this.avatar = avatar; } public Integer getSex() { return sex; } public void setSex(Integer sex) { this.sex = sex; } public Integer getType() { return type; } public void setType(Integer type) { this.type = type; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } } ``` ###### 编写测试方法 ``` public class EasyPoiExcelUtil { /** * excel导出操作 */ @Test public void exportExcel() throws IOException { List dataList = new ArrayList<>(); for (int i = 0; i < 10; i++) { UserExcel userEntity = new UserExcel(); userEntity.setNickName("张三" + i); userEntity.setAvatar("头像" + i); userEntity.setSex(i % 2 == 0 ? 1 : i % 2); userEntity.setAvatar("头像" + i); userEntity.setRealName("真实姓名" + i); userEntity.setPhone("1599485698" + i); userEntity.setCreateTime(new Date(System.currentTimeMillis() + i)); dataList.add(userEntity); } //生成excel文档 Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("用户", "用户信息"), UserExcel.class, dataList); FileOutputStream fos = new FileOutputStream("D:\\file\\excel\\easypoi-user.xls"); workbook.write(fos); fos.close(); } @Test public void exportDefinitionExcel() throws IOException { //封装表头 List entityList = new ArrayList(); entityList.add(new ExcelExportEntity("姓名", "name")); entityList.add(new ExcelExportEntity("年龄", "age")); ExcelExportEntity entityTime = new ExcelExportEntity("操作时间", "time"); entityTime.setFormat("yyyy-MM-dd HH:mm:ss"); entityTime.setWidth(20.0); entityList.add(entityTime); //封装数据体 List> dataList = new ArrayList<>(); for (int i = 0; i < 10; i++) { Map userEntityMap = new HashMap<>(); userEntityMap.put("name", "张三" + i); userEntityMap.put("age", 20 + i); userEntityMap.put("time", new Date(System.currentTimeMillis() + i)); dataList.add(userEntityMap); } //生成excel文档 Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("学生","用户信息"), entityList, dataList); FileOutputStream fos = new FileOutputStream("D:\\file\\excel\\easypoi-definition.xls"); workbook.write(fos); fos.close(); } /** * 导入excel操作 */ @Test public void importExcel() { ImportParams params = new ImportParams(); params.setTitleRows(1); params.setHeadRows(1); long start = System.currentTimeMillis(); List list = ExcelImportUtil.importExcel(new File("D:\\file\\excel\\easypoi-user1.xls"), UserExcel.class, params); System.out.println("耗时" + (System.currentTimeMillis() - start) + "秒"); System.out.println(JSONArray.toJSONString(list)); } } ``` #### EasyExcel + 简介:easyexcel 是阿里巴巴开源的一款 excel 解析工具,底层逻辑也是基于 apache poi 进行二次开发的。不同的是,再读写数据的时候,采用 sax 模式一行一行解析,在并发量很大的情况下,依然能稳定运行 ##### 相关依赖 ``` com.alibaba easyexcel 2.2.6 com.google.guava guava 29.0-jre ``` ##### 相关操作代码 ###### 相关model ``` public class UserEasyExcel { @ExcelProperty(value = "昵称") private String nickName; @ExcelProperty(value = "真实姓名") private String realName; @ExcelProperty(value = "头像") private String avatar; @ExcelProperty(value = "性别") private Integer sex; @ExcelProperty(value = "类型") private Integer type; @ExcelProperty(value = "手机号") private String phone; @ExcelProperty(value = "创建时间") private Date createTime; public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } public String getRealName() { return realName; } public void setRealName(String realName) { this.realName = realName; } public String getAvatar() { return avatar; } public void setAvatar(String avatar) { this.avatar = avatar; } public Integer getSex() { return sex; } public void setSex(Integer sex) { this.sex = sex; } public Integer getType() { return type; } public void setType(Integer type) { this.type = type; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } } ``` ###### 编写测试方法 ``` public class EasyExcelUtil { @Test public void exportExcel(){ List dataList = new ArrayList<>(); for (int i = 0; i < 10; i++) { UserEasyExcel userEntity = new UserEasyExcel(); userEntity.setNickName("张三" + i); userEntity.setAvatar("头像" + i); userEntity.setSex(i % 2 == 0 ? 1 : i % 2); userEntity.setAvatar("头像" + i); userEntity.setRealName("真实姓名" + i); userEntity.setPhone("1599485698" + i); userEntity.setCreateTime(new Date(System.currentTimeMillis() + i)); dataList.add(userEntity); } EasyExcel.write("D:\\file\\excel\\easyexcel-user1.xls", UserEasyExcel.class).sheet("用户信息").doWrite(dataList); } @Test public void exportDefinitionExcel(){ //表头 List> headList = new ArrayList<>(); headList.add(Lists.newArrayList("姓名")); headList.add(Lists.newArrayList("年龄")); headList.add(Lists.newArrayList("操作时间")); //数据体 List> dataList = new ArrayList<>(); for (int i = 0; i < 10; i++) { List data = new ArrayList<>(); data.add("张三" + i); data.add(20 + i); data.add(new Date(System.currentTimeMillis() + i)); dataList.add(data); } EasyExcel.write("D:\\file\\excel\\easyexcel-user2.xls").head(headList).sheet("用户信息").doWrite(dataList); } @Test public void importExcel(){ String filePath = "D:\\file\\excel\\easyexcel-user1.xls"; List list = EasyExcel.read(filePath).head(UserEasyExcel.class).sheet().doReadSync(); System.out.println(JSONArray.toJSONString(list)); } } ```