Excel导入导出

POI-Excel导入导出

前言:前两天工作中需要做一个报表,那么必然表格数据的导入导出是少不了的。其实真正这个需求也不是那么简单,确实是基于excel的导入导出,但是要求的是页面中用户可以在富文本编辑器上写文档,然后需要导出列表中所有文档的数据内容,难点就是富文本编辑器可以定义字体的大小,格式,表情等等,甚至还有图片;所以要想把这些完整的导出到本地,真的不好搞。确实网上有很多的工具类,但是发现的确满足不了我的需求。(说的这些其实我也还没做好(等这个功能做完再详细讲这个),这篇文章就先给大家讲一下简单格式的excel的导入和导出;大多数的统计报表其实也就是这样,没那么复杂。)

前提: excel导入导出是在springboot项目上基于依赖POI库实现的(其实也不重要,主要是介绍的代码理解)

网上有很多很多对POI封装好的工具类啦,可以直接使用的;

一 、POI介绍

首先POI提供API给Java程序对Microsoft Office格式档案读和写的功能,所以它不仅可以对excel进行读写,对word文档也是可以操作的,这里就只介绍对报表的操作的;

Excel是有两种格式的,一个是.xls,一个是.xlsx;

POI的HSSF包提供了对xls格式表格的读写功能,XSSF包提供了对xlsx格式表格的读写功能;

下面就介绍一下POI对于excel读写操作的常用类的说明:

类名说明
HSSFWorkbookexcel文档对象
HSSFSheetsheet表单
HSSFRow表单的行
HSSFCell表单的单元格
HSSFFont表单的字体
HSSFCellStyle单元格的样式
HSSFDataFormat单元格的日期格式
HSSFHeader表单的页眉
HSSFFooter表单的页脚

知道了这些常用的类的意思基本就可以读懂工具类的逻辑了,用还是要用别人的工具类的,但是如果获取的结果和自己的需求不太一样,我们至少可以对工具类做一些修改,达到自己的要求;

先直接把网上现成的ExcelUtil工具类贴出来,然后再介绍每个过程、逻辑是做什么的;

package com.leejie.utils;

import com.leejie.bean.ExcelData;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

import static org.apache.poi.ss.usermodel.CellType.*;

/**
 * 路径:com.example.demo.utils
 * 类名:
 * 功能:导入导出
 */
public class ExcelUtil {

    public static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);

    /**
     * 方法名:exportExcel
     * 功能:导出Excel
     */
    public static void exportExcel(HttpServletResponse response, ExcelData data) {
        log.info("导出解析开始,fileName:{}",data.getFileName());
        try {
            //实例化HSSFWorkbook
            HSSFWorkbook workbook = new HSSFWorkbook();
            //创建一个Excel表单,参数为sheet的名字
            HSSFSheet sheet = workbook.createSheet("sheet");
            //设置表头
            setTitle(workbook, sheet, data.getHead());
            //设置单元格并赋值
            setData(sheet, data.getData());
            //设置浏览器下载
            setBrowser(response, workbook, data.getFileName());
            log.info("导出解析成功!");
        } catch (Exception e) {
            log.info("导出解析失败!");
            e.printStackTrace();
        }
    }

    /**
     * 方法名:setTitle
     * 功能:设置表头
     */
    private static void setTitle(HSSFWorkbook workbook, HSSFSheet sheet, String[] str) {
        try {
            HSSFRow row = sheet.createRow(0);
            //设置列宽,setColumnWidth的第二个参数要乘以256,这个参数的单位是1/256个字符宽度
            for (int i = 0; i <= str.length; i++) {
                sheet.setColumnWidth(i, 15 * 256);
            }
            //设置为居中加粗,格式化时间格式
            HSSFCellStyle style = workbook.createCellStyle();
            HSSFFont font = workbook.createFont();
            font.setBold(true);
            style.setFont(font);
            style.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"));
            //创建表头名称
            HSSFCell cell;
            for (int j = 0; j < str.length; j++) {
                cell = row.createCell(j);
                cell.setCellValue(str[j]);
                cell.setCellStyle(style);
            }
        } catch (Exception e) {
            log.info("导出时设置表头失败!");
            e.printStackTrace();
        }
    }

    /**
     * 方法名:setData
     * 功能:表格赋值
     */
    private static void setData(HSSFSheet sheet, List<String[]> data) {
        try{
            int rowNum = 1;
            for (int i = 0; i < data.size(); i++) {
                HSSFRow row = sheet.createRow(rowNum);
                for (int j = 0; j < data.get(i).length; j++) {
                    row.createCell(j).setCellValue(data.get(i)[j]);
                    log.info("第 "+ (i+1) +" 行"+", 第 "+ (j+1) +" 列: --->  "+row.getCell(j));
                }
                rowNum++;
            }
            log.info("表格赋值成功!");
        }catch (Exception e){
            log.info("表格赋值失败!");
            e.printStackTrace();
        }
    }

    /**
     * 方法名:setBrowser
     * 功能:使用浏览器下载
     */
    private static void setBrowser(HttpServletResponse response, HSSFWorkbook workbook, String fileName) {
        try {
            //清空response
            response.reset();
            //设置response的Header
            response.addHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("utf8"),"ISO8859-1")+".xlsx");
            response.setContentType("application/octet-stream");
            OutputStream os = new BufferedOutputStream(response.getOutputStream());
//            response.setContentType("application/vnd.ms-excel;charset=gb2312");
            //将excel写入到输出流中
            workbook.write(os);
            os.flush();
            os.close();
            log.info("设置浏览器下载成功!");
        } catch (Exception e) {
            log.info("设置浏览器下载失败!");
            e.printStackTrace();
        }

    }


    /**
     * 方法名:importExcel
     * 功能:导入
     *
     */
    public static List<Object[]> importExcel(MultipartFile file) {
        log.info("导入解析开始,fileName:{}",file.getName());
        try {
            List<Object[]> list = new ArrayList<>();
//            InputStream inputStream = new FileInputStream(fileName);
            //获取到file的输入流
            InputStream inputStream = file.getInputStream();
            //通过excel文件输入流创建一个工作薄
            Workbook workbook = WorkbookFactory.create(inputStream);
            //获取到工作薄的第一个sheet表格
            Sheet sheet = workbook.getSheetAt(0);
            //获取sheet的行数
            int rows = sheet.getPhysicalNumberOfRows();
            for (int i = 0; i < rows; i++) {
                //过滤表头行(默认是有表头的(列表属性))
                if (i == 0) {
                    continue;
                }
                //获取当前行的数据(从第一行开始)
                Row row = sheet.getRow(i);
                //创建一个长度为当前行的列数的数组
                Object[] objects = new Object[row.getPhysicalNumberOfCells()];
                int index = 0;
                for (Cell cell : row) {
                    //判断当前行的每个单元格的类型,并将单元格类型赋值到数组[i]中
                    if (cell.getCellType() == NUMERIC.getCode()) {
                        objects[index] = (int) cell.getNumericCellValue();
                    }
                    if (cell.getCellType()==STRING.getCode()) {
                        objects[index] = cell.getStringCellValue();
                    }
                    if (cell.getCellType()==BOOLEAN.getCode()) {
                        objects[index] = cell.getBooleanCellValue();
                    }
                    if (cell.getCellType()==ERROR.getCode()) {
                        objects[index] = cell.getErrorCellValue();
                    }
                    index++;
                }
                list.add(objects);
            }
            log.info("导入文件解析成功!");
            return list;
        }catch (Exception e){
            log.info("导入文件解析失败!");
            e.printStackTrace();
        }
        return null;
    }

}

说实话这个工具类真的不错,非常适合用来对excel导入导出的学习,也适用于普通报表,而且代码逻辑还是很清晰的。可以看到每个比较重要的代码上都是由注解的(有些也是我自己理解的时候添加的),把重要的还是给大家罗列出来,比如:创建工作薄,sheet表格,获取行、列、单元格,判断每个单元格的类型等…

对了,大家看了这个工具类会发现有一个ExcelData的类,这个类是用来存放文件名,表头,数据的;

public class ExcelData {
	//省略getter,setter方法,主要看使用的数据类型
    private String fileName;
    private String[] head;
    private List data;
}
导出:

既然是导出,那肯定就要自己创建一个excel工作薄,sheet表格以及表头(每一列的属性名称),然后把数据按照顺序存到每一个单元格里面;

  1. 创建工作薄(才注意这里使用的是HSSF这个包,默认就是生成xls格式的文档了)

    HSSFWorkbook workbook = new HSSFWorkbook();
    
  2. 创建一个sheet表单(sheet名称可以自定义)

     HSSFSheet sheet = workbook.createSheet("sheet");
    
  3. 设置表头(具体实现等会介绍)

    setTitle(workbook, sheet, data.getHead());
    
  4. 给单元格赋值(具体实现等会介绍)

    setData(sheet, data.getData());
    
  5. 设置浏览器下载信息(具体实现等会介绍)

    setBrowser(response, workbook, data.getFileName());
    

整个过程就这5步,创建工作薄和表单就不说了,总体看了下也是使用了上面介绍的常用类的方法操作的,

  1. 通过HSSFSheet类创建表格首行,长度为表头数组的长度;
  2. HSSFCellStyle类设置表头样式,HSSFFont设置字体,HSSFDataFormat设置时间格式;HSSFCell创建单元格并赋值进去(就是将我们ExcelData中的head数组中的数据添加到表头的单元格,如果这里不需要表头,那就可以不设置赋值表头啦)
  3. 给表格具体内容赋值(将我们ExcelData中data列表添加到单元格中),这里就是写两个for循环,循环每一个行的每一列中就确定了一个单元格,row = sheet.createRow()来创建每一行,遍历每行,添加data值到单元格cell = row.createCell(),然后对单元格赋值row.setCellValue(data.get(i)[j]);
  4. 设置响应的浏览器信息,因为我们需要将excel响应给用户,对于response响应体的介绍其实我前面有篇http发送请求中简单介绍的有(怕看不懂也没关系,点进去里面还有推荐文章的链接可以点~~~),回到代码,这里就是设置了response的响应头(里面设置了excel导出的文件名)和响应体类型,然后就是将工作薄通过输出流发送到浏览器了。
导入:

既然是excel导入到系统中,就是遍历每个单元格读取内容咯,

  1. 前端传MultipartFile类型的文件到我们的控制层(这里前端发送请求,后端处理请求的流程也可以看我前面的文章哦~~),后端接收到文件转换成流。

    InputStream inputStream = file.getInputStream();
    
  2. 通过inputStream输入流来生成一个工作薄

    Workbook workbook = WorkbookFactory.create(inputStream);
    
  3. 获取到工作薄的第一个sheet表格(如果其中有多个sheet,我们也可以直接进行遍历每一个sheet,有方法可以获取到sheet的length的)

    Sheet sheet = workbook.getSheetAt(0);
    
  4. 获取表格的行数

    int rows = sheet.getPhysicalNumberOfRows();
    
  5. 遍历每行(如果有表头,我们需要过滤掉第0行)

  6. 获取当前行的数据

    Row row = sheet.getRow(i);
    
  7. 创建一个数组用来存放每个单元格的数据,长度就为当前行的列数

    Object[] objects = new Object[row.getPhysicalNumberOfCells()];
    
  8. 遍历当前行的每一列(也就是每个的单元格),判断每个单元格的类型(string,boolean,numeric…)然后赋值到7.创建的数组中;

  9. 最后将每一行创建的数组添加到list列表中,返回list列表即可,这样excel中的每一行数据就存在数组里,每一个数组就存在了列表中。我们需要将它存到数据库中的话,就可以直接遍历list中的每个数组然后对应赋值到一个对象中,然后就可以执行sql保存数据库啦。

关于这个工具类的来源和相关excel导入导出的推荐文章链接:https://blog.csdn.net/typ1805/article/details/83279532

https://blog.csdn.net/tanwenfang/article/details/94463709

总结

慢慢来慢慢看,我都能看懂的工具类,那就肯定不难,点开每个类源码,看看这个类有哪些方法,构造函数,也不是说要看它源码逻辑,因为我觉得这样理解的能更清楚深刻一点。12点了,该睡觉了~~
对了,只是给贴出来了一个工具类,具体的简单实现我上传到gitee上,由于比较懒,就把这个excel的导入导出和之前文件上传和下载的列子写在一个项目上了,有兴趣可以看看。就很简单实现,完全就是体验了一下这些功能,简单记录一下而已,工作中的话其实就要复杂的多了,但是怎么说,再复杂也就是这个流程,错不了。gitee地址:https://gitee.com/leejiezh/springboot-file-upload

下一篇文章:关于java的编码与解码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值