网页数据导出为Excel(带图片)

将网页数据导出为Excel恐怕是日常开发遇到非常多的需求了,以往的操作可能会稍显复杂,今天学习了一个新的神器,分享给大家,话不多说,上代码

  • 我们选用easyExcel作为技术栈,首先引入依赖
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.6</version>
  </dependency>
  • 为了方便操作,我们首先建立一个与Excel对应的实体类
package com.****.energy.nat.entity;

/**
 * @author ascool_zh
 * @create 2021-02-9:59
 */
@Data
@ContentRowHeight(35)
@HeadRowHeight(20)
public class ExcelModel {
    @ExcelProperty(value="条码号",index = 0)
    @ColumnWidth(23)
    private String barCode;
    @ExcelProperty(value="条形码",index = 1)
    @ColumnWidth(23)
    private File barCodeImg;
}
/**
 * ExcelProperty index 指定写到第几列,默认根据成员变量排序。value指定写入的名称,默认成员变量的名字,多个value可以参照快速开始中的复杂头
 * ExcelIgnore 默认所有字段都会写入excel,这个注解会忽略这个字段
 * DateTimeFormat 日期转换,将Date写到excel会调用这个注解。里面的value参照java.text.SimpleDateFormat
 * NumberFormat 数字转换,用Number写excel会调用这个注解。里面的value参照java.text.DecimalFormat
 * ExcelIgnoreUnannotated 默认不加ExcelProperty的注解的都会参与读写,加了不会参与
 */
  • 写Excel
  1. 一个简单的例子
@Data
public class DemoData {
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    private String ignore;
}
/**
     * 指定写入的列
     * <p>1. 创建excel对应的实体对象 参照{@link IndexData}
     * <p>2. 使用{@link ExcelProperty}注解指定写入的列
     * <p>3. 直接写即可
     */
    @Test
    public void indexWrite() {
        //文件名,文件会被存入到这个路径
        String fileName = TestFileUtil.getPath() + "indexWrite" + System.currentTimeMillis() + ".xlsx";
        // 这里 需要指定写用哪个class去写,DemoData,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
    }
  1. 批量写
/**
     * 重复多次写入
     * <p>
     * 1. 创建excel对应的实体对象 参照{@link ComplexHeadData}
     * <p>
     * 2. 使用{@link ExcelProperty}注解指定复杂的头
     * <p>
     * 3. 直接调用二次写入即可
     */
    @Test
    public void repeatedWrite() {
        // 方法1 如果写到同一个sheet
        String fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
        ExcelWriter excelWriter = null;
        try {
            // 这里 需要指定写用哪个class去写
            excelWriter = EasyExcel.write(fileName, DemoData.class).build();
            // 这里注意 如果同一个sheet只要创建一次
            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
            // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来
            for (int i = 0; i < 5; i++) {
                // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                List<DemoData> data = data();
                excelWriter.write(data, writeSheet);
            }
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }

        // 方法2 如果写到不同的sheet 同一个对象
        fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
        try {
            // 这里 指定文件
            excelWriter = EasyExcel.write(fileName, DemoData.class).build();
            // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面
            for (int i = 0; i < 5; i++) {
                // 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样
                WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).build();
                // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                List<DemoData> data = data();
                excelWriter.write(data, writeSheet);
            }
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }

        // 方法3 如果写到不同的sheet 不同的对象
        fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
        try {
            // 这里 指定文件
            excelWriter = EasyExcel.write(fileName).build();
            // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面
            for (int i = 0; i < 5; i++) {
                // 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样。这里注意DemoData.class 可以每次都变,我这里为了方便 所以用的同一个class 实际上可以一直变
                WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).head(DemoData.class).build();
                // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                List<DemoData> data = data();
                excelWriter.write(data, writeSheet);
            }
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }
  1. 图片写
@Data
@ContentRowHeight(100)
@ColumnWidth(100 / 8)
public class ImageData {
    private File file;
    private InputStream inputStream;
    /**
     * 如果string类型 必须指定转换器,string默认转换成string
     */
    @ExcelProperty(converter = StringImageConverter.class)
    private String string;
    private byte[] byteArray;
    /**
     * 根据url导出
     *
     * @since 2.1.1
     */
    private URL url;
}
/**
     * 图片导出
     * <p>
     * 1. 创建excel对应的实体对象 参照{@link ImageData}
     * <p>
     * 2. 直接写即可
     */
    @Test
    public void imageWrite() throws Exception {
        String fileName = TestFileUtil.getPath() + "imageWrite" + System.currentTimeMillis() + ".xlsx";
        // 如果使用流 记得关闭
        InputStream inputStream = null;
        try {
            
            List<ImageData> list = new ArrayList<ImageData>();
            ImageData imageData = new ImageData();
            list.add(imageData);
            String imagePath = TestFileUtil.getPath() + "converter" + File.separator + "img.jpg";
            // 放入五种类型的图片 实际使用只要选一种即可
            imageData.setFile(new File(imagePath));
            inputStream = FileUtils.openInputStream(new File(imagePath));
            imageData.setString(imagePath);
            imageData.setInputStream(inputStream);
                        imageData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath)));
            imageData.setUrl(new URL(
                "https://raw.githubusercontent.com/alibaba/easyexcel/master/src/test/resources/converter/img.jpg"));
            
            
            EasyExcel.write(fileName, ImageData.class).sheet().doWrite(list);
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }
  1. Web写
@RequestMapping("/exportExcel")
	public void exportExcel(String startCode, int count, String netCode,String netName,HttpServletResponse response) throws IOException {
        //后台查出的数据
		List<Map<String, Object>> qryPrintList = printBarcodeService.getPrintList(startCode, count, netCode);
        //创建存放ExcelModel的List,你可以理解为一个ExcelModel为一行数据,其属性barCode和barCodeImg即是行所要填的值
		List<ExcelModel> list = new ArrayList<ExcelModel>();
		String msg = "";
		String path = "";
		if (count <= 5000) {
			for (Map<String, Object> map : qryPrintList) {
				msg = (String) map.get("barCode");
				path = barcodeFilepath + msg + ".png";
                ExcelModel excelModel = new ExcelModel();
				try {
                    //对ExcelModel对象赋值,相当于你填写每行的记录
					list.add(excelModel);
                    //msg和path都已经赋值
					excelModel.setBarCode(msg);
					excelModel.setBarCodeImg(new File(path));
				} catch (Exception e) {
					System.out.println(e);
				}
			}
            //设置一些属性
			response.setContentType("application/vnd.ms-excel");
			response.setCharacterEncoding("utf-8");
			// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
			String fileName = URLEncoder.encode(netName+"条码表", "UTF-8").replaceAll("\\+", "%20");
			response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            //执行,list中已经包含多个ExcelModel,即相当于填写了多条记录
			EasyExcel.write(response.getOutputStream(), ExcelModel.class).sheet("条码表").doWrite(list);
		}

	}
//前端避免使用Ajax,直接用window.location.href更方便
function exportExcel(){
	var startCode = $("#startCode").val();
	var count = $("#count").val();
	var netCode = $("#checkNet").val();
	var netName = $("#checkNet").combobox('getText');
	window.location.href = "nat/print/exportExcel?startCode="+startCode+"&count="+count+"&netCode="+netCode+"&netName="+netName;

}