使用POI实现excel导出功能

Excel的文件格式有两种:

  1. xls格式,这是office2003及之前使用的Excel格式
  2. xlsx格式,office2007及之后使用的Excel格式。

.xlsx文件比.xls的压缩率高,也就是相同数据量下,.xlsx的文件会小很多。

1.如果需要创建xls格式的excel需要使用:

// 一个工作表最大行数65536,最大列列256
Workbook wb = new HSSFWorkbook()

2.如果去要创建xlsx格式的excel需要使用:

// 一个工作表最大行數1048576,最大列数16384
Workbook wb = new XSSFWorkbook()

3.如果数据达到上万可使用 ,防止OOM(内存溢出)

// 内存中保留10000条数据,其余写入硬盘临时文件
Workbook wb = new SXSSFWorkbook(10000);

我在一次工作中,需要将数据以excel的格式进行导出。

花了一天的时间进行代码的完善(反射),想着以后也可能用到,所以今天就把代码记录下。pom.xml特别强调需要引入的jar包:

<!--servlet 等会用于获取响应对象,请求对象-->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <!--<scope>provided</scope>-->
    </dependency>
   <!-- Excel操作包 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>3.17</version>
    </dependency>
    <!--使用SXSSFWorkbook-->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>3.15-beta2</version>
    </dependency>

话不多说上代码:

控制器导出,需要你们自己写的代码:

/**
 * 导出
*/
@RequestMapping("/exportExcel")
@ResponseBody
public void exportExcel(){
	int n = 200000;
	List<User> list = new LinkedList<User>();
	for(int i = 0; i < n; i++){
		User user = new User(i,i,"name"+i);
		list.add(user);
	}
    // 下面这四行必须要写。
	String fileds[] = {"序号","年龄","姓名"};
	String attr[] = {"id","age","name"};
	ExportExcel.setFilesAndAttributes(fileds,attr);
	ExportExcel.writeExcel("test", list);
}

工具类:

package com.mf.util;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.List;

/**
 * 导出集合数据  到excel文件
 * excel文件:
 *      文件名,表头名字, 实体数据。
 *  1.需要设置表头的文件名,=。
 *  2.需要设置表头的字段,这里就是fields[] 数组,attributes[] 就是存放个表头对应对象的字段
 *    导出集合到excel中,通常集合中对象的所有属性不全部导出到excel,例如id字段。
 *  3.需要设置实体数据。
 *  注意: fields [] 和 attributes[] 遵循一一对应,即表头的一个格子对应对象的一个字段。
 *  另外,这里使用的是反射做处理的,所以约定优于配置,即集合中的实体对象需要和attributes[]一样的顺序(只许多,不许少)
 *  例如:
 *      fields={"序号","姓名","年龄"}
 *      attributes={"id","name","age"}
 * @ClassName projectExportExcel
 * @Description TODO
 * @Author cfl
 * @Date 2019/8/6 19:35
 * @Version 1.0 : 利用反射结合POI.jar 中HSSFWorkbook对象导出Excel。
 * @Version 1.1 (19/10/2): 一、新增 成员变量 suffix(文件后缀)、SIZE(判断大小,选择不同的Workbook对象)、MEMORY_NUM(内存中保留数据条数,其余写入硬盘临时文件)、
 *                  SHEET_SIZE(一个工作薄sheet多少条数据);
 *              二、根据接口编程,根据里氏代换原则动态选择 HSSFWorkbook,XSSFWorkbook, SXSSFWorkbook对象;
 *              三、完善代码,使其不再依赖pojo的声明字段的顺序;
 *              四、删除 void setFilesAndAttributes(Class clazz) 方法;
 *              五、新增 void setSHEET_SIZE(int sheet_size)方法;
 */
public class ExportExcel {
	// 日志
	private static final Logger logger = LoggerFactory.getLogger(ExportExcel.class);
	// 表头
	private static String[] Fields;
	// 表头对应的 字段
	private static String[] Attributes;
	// excel文件格式(.xls,.xlsx)
	private static String suffix = ".xlsx";
	// 导出时选择不同的工作博对象,小于等于SIZE 创建HSSFWorkbook,大于SIZE创建SXSSFWorkbook
	private final static  int SIZE = 10000;
	// 创建SXSSFWorkbook对象对少条flush
	private final static  int MEMORY_NUM = 1000;
	// 一个工作表有 SHEET_SIZE 数据(加一是因为表头占一行)
	private static  Integer SHEET_SIZE = 100001;


	/**
	 * 设置成员变量 fields 和attributes
	 * @param fileds 表头
	 * @param attributes    表头对应的字段
	 */
	public static void setFilesAndAttributes(String[] fileds, String[] attributes) {
		// 这是复制了一份地址,指向的还是同一个对象,原数组改变,这里也要改变
		//ExportExcel.Fields = fileds;
		//ExportExcel.Attributes = attributes;
		// 深层复制。开辟一块新内存空间
		// 先初始化数组
		int length = fileds.length;
		ExportExcel.Fields = new String[fileds.length];
		ExportExcel.Attributes = new String[attributes.length];
		// 因 fileds,attributes一一对应。
		for(int i = 0; i < length; i++) {
			ExportExcel.Fields[i] = fileds[i];
			ExportExcel.Attributes[i] = attributes[i];
		}
	}

	/**
	 * 根据excel数据的多少,设置一个工作薄有多少条数据
	 * @param sheet_size
	 */
	public static void setSHEET_SIZE(int sheet_size){
		SHEET_SIZE = sheet_size;
	}

	/**
	 * 写出Excel文件
	 * 	fileName:文件下载到客户端的文件名
	 * @param fileName  浏览器下载的excel文件名字
	 * @param list  需要写出的数据
	 * @param <T>
	 */
	public static <T> void writeExcel(String fileName, List<T> list) {
        // 1.判断属性 files 和 attributes 属性是否满足导出excel的基本要求。
        if(!checkFieldsAndAttr()){
            return;
        }
        // 获取请求对象和响应对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();

		// 2.创建工作薄对象
		Workbook wb = ExportExcel.createWorkbook(list);
		// 查看内存使用
		System.out.println("创建完成");
		System.err.println("总的内存->" + Runtime.getRuntime().totalMemory() / 1024 / 1024 + "MB");
		System.err.println("剩余的内存->" + Runtime.getRuntime().freeMemory() / 1024 / 1024 + "MB");
		//响应对象获取输出流,通过输出流写出excel文件
		OutputStream out = null;
		// 3.设置编码格式,响应头
		try {
			request.setCharacterEncoding("UTF-8");
			response.setCharacterEncoding("UTF-8");
			response.setContentType("application/x-download");
			// 获取excel名字
			fileName = setFileName(fileName);
			fileName = URLEncoder.encode(fileName, "UTF-8");
			response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
			out = response.getOutputStream();
			// 写出到浏览器
			wb.write(out);
		}catch (RuntimeException e){
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			try {
				if(out!=null){
					out.close();
				}
				wb.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 检查属性Fields 和 Attributes 是否满足导出excel的要求
	 * @return  满足导出要求,返回true,不满足返回false。
	 */
	private static boolean checkFieldsAndAttr(){
		Boolean flag = true;

		// Fields 和 Attributes不一一对应!
		if(ExportExcel.Fields == null || ExportExcel.Attributes == null){
			flag = false;
			System.err.println("ExportException: 导出excel时的 Fields 和 Attributes 未定义");
		}else if(ExportExcel.Fields.length != ExportExcel.Attributes.length){
			flag = false;
			System.err.println("ExportException: 导出excel时的Fields 和 Attributes 的长度不一致错误");
		}
		return flag;
	}

	/**
	 * 返回 HSSFWorkbook/ XSSFWorkbook/SXSSFWorkbook 对象
	 * 创建Excel文件对象
	 * @Param [list 表格数据, fields, attributes]
	 * @Return org.apache.poi.hssf.usermodel.HSSFWorkbook
	 */
	private static <T> Workbook createWorkbook(List<T> list) {
		//创建Workbook对象
		Workbook wb = null;
		//创建HSSFSheet对象

		if(list.size() <= SIZE){
			// 2003
			wb = new HSSFWorkbook();
			// 修改excel文件格式。
			suffix = ".xls";
		} else if(list.size() > SIZE){
			// 进行大批量写入操作解决了这个问题。防止内存溢出,构造参数是指内存中多少条flush到磁盘上
			try {
				//ExportExcel.createTempXSSFWorkbook(),
				wb = new SXSSFWorkbook(MEMORY_NUM);
			} catch (Exception e) {
				e.printStackTrace();
			}
			System.err.println("创建了SXSSFWorkbook");
		} else {
			// 2007以上,先将其保留
			wb = new XSSFWorkbook();
		}
		// 创建内容实体
		createBody(wb, list);
		// 写出excel到硬盘
//		writeExcelTOHardDisk(wb);
		return wb;
	}


	/**
	 * 根据反射创建excel 的实体
	 * 注意这里的list集合的成员的属性,需要根据attributes顺序写。
	 * @param wb    excel工作博对象
	 * @param list  需要写出到excel的集合
	 * @param <T>   集合泛型
	 */
	private static <T> void createBody(Workbook wb, List<T> list) {
		// excel工作表对象
		Sheet sheet = null;
		// excel行对象
		Row row = null;
		// 单元格对象
		Cell c = null;
		// 使用泛型声明一个集合中的元素类型对象 P
		T p = null;
		// P对象的所有字段
		Field[] fields = null;
		// 新建的sheet工作表 从第一行创建起
		int row_index = 0;
		// 创建行
		for (int i = 0; i < list.size(); i++) {
			// SHEET_SIZE 条数据 换一个工作表
			if(i % SHEET_SIZE == 0){
				System.gc();
				row_index = 0;
				System.out.println(i);
				sheet = wb.createSheet("sheet"+ (i / SHEET_SIZE));
				// 每一个工作薄都要创建表头
				row = sheet.createRow(row_index);
				// 创建表头
				createHead(row, ExportExcel.Fields);
			}
			++row_index;
			row = sheet.createRow(row_index);
			// 从集合中获取需要导出的一条数据
			p = list.get(i);
			// 获取对象所有属性
			fields = p.getClass().getDeclaredFields();

			// 创建一行中的所有列
			try {
				for (int j = 0; j < fields.length; j++) {
					for (int k = 0; k < ExportExcel.Attributes.length; k++) {
						// 配置文件字段和对象字段相比较,相同就执行方法
						if (fields[j].getName().equalsIgnoreCase(ExportExcel.Attributes[k])) {
							// 创建HSSFCell对象 表头
							c = row.createCell(k);
							fields[j].setAccessible(true);
							if (fields[j].get(p) == null) {
								c.setCellValue("");
							} else {
								c.setCellValue(fields[j].get(p).toString());
							}
							break;
						}
					}
				}
			} catch (IllegalAccessException e) {
				//logger.error("导出Excel,在设置实体部分时出错!");
				e.printStackTrace();
			}
		}
	}

	/**
	 * 创建 Excel表头
	 * @param row   表头行对象
	 * @param fields   表头列字段
	 */
	private static void createHead(Row row, String[] fields) {
		// 创建行头
		for (int i = 0; i < fields.length; i++) {
			//创建HSSFCell对象 表头
			Cell cell = row.createCell(i);
			//设置表头值
			cell.setCellValue(fields[i]);
		}
	}

	/**
	 * 设置导出文件名
	 *
	 * @Param [fileName] 文件名
	 * @Return java.lang.String
	 */
	private static String setFileName(String fileName) {
		// 文件名包含了.xls,和.xlsx;直接使用
		if(fileName.endsWith(".xls") || fileName.endsWith(".xlsx")){
			return fileName;
		}
		// 默认 xlsx 格式
		if (fileName.indexOf(".") == -1) {
			fileName += suffix;
		} else if(fileName.endsWith(".")){
			// 截取小数点前面的字符串。
			fileName = fileName.substring(0, fileName.lastIndexOf("."));
			// 追加文件格式
			fileName += suffix;
		}else{
			// 抛出异常
			throw new RuntimeException("警告:导出文件错误: " + fileName +"不是正确的excel格式:.xls, .xlsx");
		}

		return fileName;
	}

	/**
	 * 使用SXSSFWorkboo时,需要一个临时存储数据的文件。
	 * 创建一个临时存储数据XSSFWorkbook对象。
	 * @return
	 * @throws IOException
	 */
	private static XSSFWorkbook createTempXSSFWorkbook() throws IOException {
		InputStream inputStream = ExportExcel.class.getResourceAsStream("/xssf_temp.xlsx");
		if(inputStream == null){
			throw new RuntimeException("在导出Excel文件时,因为resources目录下临时文件 xssf_temp.xlsx 不存在!");
		}
		XSSFWorkbook book = new XSSFWorkbook(inputStream);
		return book;
	}

	/**
	 * 写出excel到硬盘
	 * @Param [wb,excel对象]
	 * @Return void
	 */
	private static void writeExcelTOHardDisk(HSSFWorkbook wb) {
		FileOutputStream output = null;
		try {
			output = new FileOutputStream("d:\\AA/workbook.xls");
			wb.write(output);
			output.flush();
			//logger.info("成功");
		} catch (FileNotFoundException e) {
			//logger.info("失败");
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}


}

 

完成 

  

发布了47 篇原创文章 · 获赞 17 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_42428264/article/details/99163178