EasyExcel

前言

EasyExcel的知识点记录及使用;
提供自己学习

一、EasyExcel是什么?

1、概述:

EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。

2、案例分析

2.1 导入依赖

 <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.1.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>poi-ooxml-schemas</artifactId>
                    <groupId>org.apache.poi</groupId>
                </exclusion>
            </exclusions>
</dependency>

2.2 读excel数据到内存中

2.2.1 代码一
	public AjaxResult importData(MultipartFile file) throws Exception {
	    //配置excel分析监听器
		ExcelListener excelListener = new ExcelListener(tbClueService);
		//读取excel表格数据
		EasyExcel.read(file.getInputStream(), TbClueExcelVo.class, excelListener).sheet().doRead();
		return AjaxResult.success(excelListener.getResult());
	}

分析
(1)格式:

  • 配置excel分析监听器
		ExcelListener excelListener = new ExcelListener(tbClueService);
因为监听器不是单例的,每次读取一条数据就会创建一个监听器对象,所有不能交给spring管理,
所以一般会提供监听器(ExcelListener )的有参构造方法,由此传入需要使用的service层的接口,
该接口的实现类为单例,被spring管理,只能通过此方法传入。
  • 读取 excel表格数据
	// 在读的时候只需要new ExcelListener 监听器传入就行了 
	EasyExcel.read(file.getInputStream(), TbClueExcelVo.class, new ExcelListener())
	    // 需要读取批注 默认不读取
	    .extraRead(CellExtraTypeEnum.COMMENT)
	    // 需要读取超链接 默认不读取
	    .extraRead(CellExtraTypeEnum.HYPERLINK)
	    // 需要读取合并单元格信息 默认不读取
	    .extraRead(CellExtraTypeEnum.MERGE).sheet().doRead();

这个框架底层使用了建造者模式,每次读取一条数据,也就将一条数据封装到一个对象中(并且每次创
建一个excel分析监听器对象和数据对象)

(2)excel监听器代码

package com.huike.clues.utils.easyExcel;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.huike.clues.domain.dto.ImportResultDTO;
import com.huike.clues.domain.vo.TbClueExcelVo;
import com.huike.clues.service.ITbClueService;

/**
 * EasyExcel监听器,用于解析数据并执行操作
 */
public class ExcelListener extends AnalysisEventListener<TbClueExcelVo> {

	/**
	 * 利用构造方法获取对应的service
	 */
	public ITbClueService clueService;

	private ImportResultDTO resultDTO;

    //每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
    private static final int BATCH_COUNT = 5;
    List<DemoData> list = new ArrayList<DemoData>();

	/**
	 * 提供带参构造方法,在这里需要通过构造方法的方式获取对应的service层
	 * 谁调用这个监听器谁提供需要的service
     * 原因:(ExcelListener这个类不能交给spring管理,因为每次读取一条数据都需要new 
     * ExcelListener这个对象和数据对象)
	 * @param clueService
	 */
	public ExcelListener(ITbClueService clueService) {
		this.clueService = clueService;
		this.resultDTO = new ImportResultDTO();
	}

	/**
	 * 每解析一行数据都要执行一次
	 * 每条都执行一次插入操作
	 * @param data
	 * @param context
	 */
	@Override
	public void invoke(TbClueExcelVo data, AnalysisContext context) {
	    //对于每一条数据的处理
		list.add(data);
        // 在这里可以做一些其他的操作  就考自己去拓展了
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            list.clear();
        }
	}

	/**
	 * 当所有数据都解析完成后会执行
	 * @param context
	 */
	@Override
	public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        // 当然也可以有很多其他操作 比如有些需要触发其他的 可以在这里调用
        saveData();
    }

 // 读取条额外信息:批注、超链接、合并单元格信息等
    @Override
    public void extra(CellExtra extra, AnalysisContext context) {
        LOGGER.info("读取到了一条额外信息:{}", JSON.toJSONString(extra));
        switch (extra.getType()) {
            case COMMENT:
                LOGGER.info("额外信息是批注,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(), extra.getColumnIndex(),
                    extra.getText());
                break;
            case HYPERLINK:
                if ("Sheet1!A1".equals(extra.getText())) {
                    LOGGER.info("额外信息是超链接,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(),
                        extra.getColumnIndex(), extra.getText());
                } else if ("Sheet2!A1".equals(extra.getText())) {
                    LOGGER.info(
                        "额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{},"
                            + "内容是:{}",
                        extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(),
                        extra.getLastColumnIndex(), extra.getText());
                } else {
                    Assert.fail("Unknown hyperlink!");
                }
                break;
            case MERGE:
                LOGGER.info(
                    "额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{}",
                    extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(),
                    extra.getLastColumnIndex());
                break;
            default:
        }
    }
    
    //在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
    @Override
    public void onException(Exception exception, AnalysisContext context) {
        // 如果是某一个单元格的转换异常 能获取到具体行号
        // 如果要获取头的信息 配合invokeHeadMap使用
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
            LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),
                excelDataConvertException.getColumnIndex());
        }
    }


	/**
	 * 返回结果集对象
	 * @return
	 */
	public ImportResultDTO getResult(){
		return resultDTO;
	}
}

分析监听器AnalysisEventListener< T>
AnalysisEventListener< T>继承了 ReadListener ,我们一般使用它的父接口

public interface ReadListener<T> extends Listener {
	//在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。
	//如果这里不抛出异常则 继续读取下一行。
    void onException(Exception var1, AnalysisContext var2) throws Exception;
    // 调用invokeHeadMap来获取表头数据
    void invokeHead(Map<Integer, CellData> var1, AnalysisContext var2);
	// 一行行读取表格内容(每一条数据解析都会调用)
    void invoke(T var1, AnalysisContext var2);
    //读取条额外信息:批注、超链接、合并单元格信息等
    void extra(CellExtra var1, AnalysisContext var2);
	// 当数据全部解析完后,此方法会执行
    void doAfterAllAnalysed(AnalysisContext var1);
    //验证是否还有数据
    boolean hasNext(AnalysisContext var1);
}

2.3 将数据写入excel中

(1)创建每条数据对应的数据对象

@Data
public class DemoData {
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    private String ignore;
}

(2)上代码

/**
 * 最简单的写
 * <p>1. 创建excel对应的实体对象 参照{@link DemoData}
 * <p>2. 直接写即可
 */
@Test
public void simpleWrite() {
    // 写法1
    String fileName = TestFileUtil.getPath() + "simpleWrite" + 
    System.currentTimeMillis() + ".xlsx";
   // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 
   //然后文件流会自动关闭
    // 如果这里想使用03 则 传入excelType参数即可
    EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());

    // 写法2
    fileName = TestFileUtil.getPath() + "simpleWrite" + 
    System.currentTimeMillis() + ".xlsx";
    // 这里 需要指定写用哪个class去写
    ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build();
    WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
    excelWriter.write(data(), writeSheet);
    // 千万别忘记finish 会帮忙关闭流
    excelWriter.finish();
}

以下是poi导入导出excel的代码(工具类)

package org.common.util;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFDataFormat;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.foreveross.common.exception.UdpBootException;
import org.foreveross.common.param.TemplateExportParamsExtend;
import org.framework.poi.excel.annotation.Excel;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @description: poi util
 */
@Slf4j
public class PoiUtil {

    private final static String EXCEL2003 = "xls";
    private final static String EXCEL2007 = "xlsx";

    /**
     * @description: 根据word模版生成word文件
     * @param: [url(模版文件类路径), fileName(要传回给浏览器的文件名称), params]
     * @return: void
     * @version: 1.0
     */
    public static void exportWordByTemplate(String url, String fileName, Map params) throws Exception {
            HttpServletResponse response = getResponse(MediaType.APPLICATION_OCTET_STREAM_VALUE, fileName);
            OutputStream outputStream = response.getOutputStream();
            ClassLoader defaultClassLoader = ClassUtils.getDefaultClassLoader();
            InputStream resourceAsStream = defaultClassLoader.getResourceAsStream(url);
            ParseWord07Extend parseWord07Extend = new ParseWord07Extend();
            XWPFDocument xwpfDocument = parseWord07Extend.parseWord(resourceAsStream, params);
            xwpfDocument.write(outputStream);
            outputStream.flush();
    }

    /**
     * @description: 根据word模版生成word文件 并返回流
     * @param: [url(模版文件类路径), fileName(要传回给浏览器的文件名称), params]
     * @return: void
     * @version: 1.0
     */
    public static OutputStream createWordByTemplateReturnOutputStream(String url, Map params) throws Exception {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ClassLoader defaultClassLoader = ClassUtils.getDefaultClassLoader();
        InputStream resourceAsStream = defaultClassLoader.getResourceAsStream(url);
        Asserts.notNull(resourceAsStream,"未找到模板文件:"+url);
        ParseWord07Extend parseWord07Extend = new ParseWord07Extend();
        XWPFDocument xwpfDocument = parseWord07Extend.parseWord(resourceAsStream, params);
        xwpfDocument.write(outputStream);
        return outputStream;
    }

    /**
     * @description: 文件下载
     * @param: [url(模版文件类路径), fileName(要传回给浏览器的文件名称), params]
     * @return: void
     * @version: 1.0
     */
    public static void exportFile(String url, String fileName) throws Exception {
        log.info("附件转换成流:{},{}",url,fileName);
        HttpServletResponse response = getResponse(MediaType.APPLICATION_OCTET_STREAM_VALUE, fileName);
        OutputStream outputStream = response.getOutputStream();
        URL urlObject = new URL(url);
        //生产环境ssl解决
        HttpURLConnection conn = (HttpURLConnection) urlObject.openConnection();
        if (conn instanceof HttpsURLConnection)  {
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom());
            ((HttpsURLConnection) conn).setSSLSocketFactory(sc.getSocketFactory());
            ((HttpsURLConnection) conn).setHostnameVerifier(new TrustAnyHostnameVerifier());
        }
        conn.connect();
        InputStream inputStream = conn.getInputStream();

        // 1K的数据缓冲
        byte[] bs = new byte[1024];

        // 读取到的数据长度
        int len;

        // 开始读取
        while ((len = inputStream.read(bs)) != -1) {
            outputStream.write(bs, 0, len);
        }

        outputStream.flush();
    }

    /**
     * @description:    根据模版导出excel
     * @param:       [url(模版地址填写类路径), fileName(要返回的文件名)
     * , entitys(数据), paramMap(参数), pojo(数据类型)]
     * @return:       void
     * @version:        1.0
     */
    public static void exportExcelByTemplate(String url, String fileName, List<Object> entitys,Map<String,Object> paramMap,Class pojo) throws Exception {
            HttpServletResponse response = getResponse(MediaType.APPLICATION_OCTET_STREAM_VALUE, fileName);
            ClassLoader defaultClassLoader = ClassUtils.getDefaultClassLoader();
            InputStream resourceAsStream = defaultClassLoader.getResourceAsStream(url);
            TemplateExportParamsExtend templateExportParams = new TemplateExportParamsExtend();
            templateExportParams.setTemplateInputStream(resourceAsStream);
            templateExportParams.setHeadingRows(2);
            ExcelExportOfTemplateExtensUtil excelExportOfTemplateUtil = new ExcelExportOfTemplateExtensUtil();
            Workbook workbook = excelExportOfTemplateUtil.createExcleByTemplate(templateExportParams, pojo, entitys, paramMap);
            OutputStream outputStream = response.getOutputStream();
            workbook.write(outputStream);
            outputStream.flush();
    }

    /**
     * @description:    根据模版导出excel
     * @param:       [url(模版地址填写类路径), fileName(要返回的文件名)
     * , entitys(数据), paramMap(参数), pojo(数据类型)]
     * @return:       void
     * @version:        1.0
     */
    public static void exportExcelByTemplateExtends(String url, String fileName, List<Object> entitys,Map<String,Object> paramMap,
                                                    Class pojo,TemplateExportParamsExtend templateExportParams) throws Exception {
        HttpServletResponse response = getResponse(MediaType.APPLICATION_OCTET_STREAM_VALUE, fileName);
        ClassLoader defaultClassLoader = ClassUtils.getDefaultClassLoader();
        InputStream resourceAsStream = defaultClassLoader.getResourceAsStream(url);
        templateExportParams.setTemplateInputStream(resourceAsStream);
        ExcelExportOfTemplateExtensUtil excelExportOfTemplateUtil = new ExcelExportOfTemplateExtensUtil();
        Workbook workbook = excelExportOfTemplateUtil.createExcleByTemplate(templateExportParams, pojo, entitys, paramMap);
        OutputStream outputStream = response.getOutputStream();
        workbook.write(outputStream);
        outputStream.flush();
    }

    /**
     * 导出excel
     * @param list          数据集合
     * @param cls           导出表头
     */
    public static <T> void exportExcel(OutputStream outputStream ,List<T> list, Class<T> cls,List<String> ignoreFields) {
        //获取表头
        Field[] fields = cls.getDeclaredFields();
        List<Field> fieldList = Arrays.stream(fields)
                .filter(field -> {
                    if(!CollectionUtils.isEmpty(ignoreFields) && ignoreFields.contains(field.getName())){
                        return false;
                    }
                    //获取标记@Excel注解的字段
                    Excel annotation = field.getAnnotation(Excel.class);
                    if (annotation != null) {
                        field.setAccessible(true);
                        return true;
                    }
                    return false;
                }).collect(Collectors.toList());
        //创建excel
        Workbook wb = new XSSFWorkbook();
        //创建sheet
        Sheet sheet = wb.createSheet("Sheet1");

        AtomicInteger ai = new AtomicInteger();
        {
            Row row = sheet.createRow(ai.getAndIncrement());
            AtomicInteger aj = new AtomicInteger();
            //写入
            fieldList.forEach(field -> {
                Excel annotation = field.getAnnotation(Excel.class);
                String columnName = "";
                if (annotation != null) {
                    columnName = annotation.name();
                }
                Cell cell = row.createCell(aj.getAndIncrement());
                //设置样式
                CellStyle cellStyle = wb.createCellStyle();
                //内容居中
                cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
                cell.setCellStyle(cellStyle);
                cell.setCellValue(columnName);
            });
        }
        if (!list.isEmpty()){
            list.forEach(t->{
                Row row1 = sheet.createRow(ai.getAndIncrement());
                AtomicInteger aj = new AtomicInteger();

                fieldList.forEach(field -> {
                    Class<?> type = field.getType();
                    Object value = "";
                    try {
                        value = field.get(t);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    Cell cell = row1.createCell(aj.getAndIncrement());
                    if (value != null) {
                        cell.setCellValue(value.toString());
                    }
                });
            });
        }
        try {
            wb.write(outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * @description:    根据邀请函模版生成PDF文件
     * @param:       [url, params]
     * @return:       java.io.InputStream
     * @version:        1.0
     */
    public static OutputStream createPDFByTemplate(String url,Map params) throws Exception{
        ClassPathResource classPathResource = new ClassPathResource(url);
        InputStream fiKeyFile = classPathResource.getInputStream();

//
        PdfReader reader = new PdfReader(fiKeyFile);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        /* 将要生成的目标PDF文件名称 */
        PdfStamper ps = new PdfStamper(reader, bos);
        PdfContentByte under = ps.getUnderContent(1);

        /* 使用中文字体 */
        BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        ArrayList<BaseFont> fontList = new ArrayList<BaseFont>();
        fontList.add(bf);

        /* 取出报表模板中的所有字段 */
        AcroFields fields = ps.getAcroFields();
        fields.setSubstitutionFonts(fontList);
        fillData(fields,params);

        /* 必须要调用这个,否则文档不会生成的 */
        ps.setFormFlattening(true);
        ps.close();

        // 转换流
        bos.close();

        return bos;
    }

    /**
     * @description:    模版填充
     * @param:       [fields, data]
     * @return:       void
     * @version:        1.0
     */
    public static void fillData(AcroFields fields, Map<String, String> data)
            throws IOException, DocumentException {
        for (String key : data.keySet()) {
            String value = data.get(key);
            fields.setField(key, value); // 为字段赋值,注意字段名称是区分大小写的  
        }
    }

    /**
     * @description:    转换流
     * @param:       [out]
     * @return:       java.io.ByteArrayInputStream
     * @version:        1.0
     */
    public static ByteArrayInputStream parse(OutputStream out) throws Exception {
        ByteArrayOutputStream baos = null;
        baos = (ByteArrayOutputStream) out;
        ByteArrayInputStream swapStream = new ByteArrayInputStream(baos.toByteArray());
        return swapStream;
    }

    /**
     * 上传excel文件转化实体类
     * @param file
     * @param cls
     * @return
     * @throws Exception
     */
    public static <T> List<T> readExcel(MultipartFile file, Class<T> cls) throws Exception {
        String fileName = file.getOriginalFilename();
        if (!fileName.matches("^.+\\.(?i)(xls)$") && !fileName.matches("^.+\\.(?i)(xlsx)$")) {
            throw new UdpBootException("上传文件格式不正确:" + fileName) ;
        }
        InputStream is = file.getInputStream();
        return readExcel(is, cls, fileName);
    }


    /**
     * 导出excel
     * @param response      HttpServletResponse
     * @param list          数据集合
     * @param cls           导出表头
     * @param name          文件名
     */
    public static <T> void exportExcel(HttpServletResponse response, List<T> list, Class<T> cls, String name){
        //获取表头
        Field[] fields = cls.getDeclaredFields();
        List<Field> fieldList = Arrays.stream(fields)
                .filter(field -> {
                    //获取标记@Excel注解的字段
                    Excel annotation = field.getAnnotation(Excel.class);
                    if (annotation != null) {
                        field.setAccessible(true);
                        return true;
                    }
                    return false;
                }).collect(Collectors.toList());
        //创建excel
        Workbook wb = new XSSFWorkbook();
        //创建sheet
        Sheet sheet = wb.createSheet("Sheet1");

        AtomicInteger ai = new AtomicInteger();
        {
            Row row = sheet.createRow(ai.getAndIncrement());
            AtomicInteger aj = new AtomicInteger();
            //写入
            fieldList.forEach(field -> {
                Excel annotation = field.getAnnotation(Excel.class);
                String columnName = "";
                if (annotation != null) {
                    columnName = annotation.name();
                }
                Cell cell = row.createCell(aj.getAndIncrement());
                //设置样式
                CellStyle cellStyle = wb.createCellStyle();
                //内容居中
                cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
                cell.setCellStyle(cellStyle);
                cell.setCellValue(columnName);
            });
        }
        if (!list.isEmpty()){
            //时间样式
            CellStyle cellStyle = wb.createCellStyle();
            XSSFDataFormat format = (XSSFDataFormat) wb.createDataFormat();
            cellStyle.setDataFormat(format.getFormat("yyyy/MM/dd"));

            list.forEach(t->{
                Row row1 = sheet.createRow(ai.getAndIncrement());
                AtomicInteger aj = new AtomicInteger();

                fieldList.forEach(field -> {
                    Class<?> type = field.getType();
                    Object value = "";
                    try {
                        value = field.get(t);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    Cell cell = row1.createCell(aj.getAndIncrement());
                    if (value != null) {
                        if (type == Date.class){
                            cell.setCellStyle(cellStyle);
                            cell.setCellValue((Date) value);
                        }else {
                            cell.setCellValue(value.toString());
                        }
                    }
                });
            });
        }
        String excelName = name +".xlsx";
        buildExcelDocument(excelName,wb,response);
    }


    private static HttpServletResponse getResponse(String contentType,String fileName) throws UnsupportedEncodingException {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)
                RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = servletRequestAttributes.getResponse();
        response.setContentType(contentType);
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
        return response;
    }

    private static final String WORD_CONTENT_TYPE = "application/msword";

    private static final String EXCEL_CONTENT_TYPE = "application/excel";

    /**
     * 浏览器下载excel
     * @param fileName
     * @param wb
     * @param response
     */
    private static  void  buildExcelDocument(String fileName, Workbook wb, HttpServletResponse response){
        try {
            response.reset();
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
            response.flushBuffer();
            wb.write(response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * excel文件流转化实体类
     * @param is
     * @param cls
     * @param fileName
     * @return
     */
    private static <T> List<T> readExcel(InputStream is, Class<T> cls, String fileName) {
        List<T> dataList = new ArrayList<>();
        Workbook workbook = null;
        try {
            if (fileName.endsWith(EXCEL2007)) {
                workbook = new XSSFWorkbook(is);
            }
            if (fileName.endsWith(EXCEL2003)) {
                workbook = new HSSFWorkbook(is);
            }
            if (workbook != null) {
                Map<String, List<Field>> classMap = new HashMap<>();
                List<Field> fields = Stream.of(cls.getDeclaredFields()).collect(Collectors.toList());
                fields.forEach(
                        field -> {
                            Excel annotation = field.getAnnotation(Excel.class);
                            //获取实体字段
                            if (annotation != null) {
                                String columnsName = annotation.name();
                                if (StringUtils.isBlank(columnsName)) {
                                    return;
                                }
                                if (!classMap.containsKey(columnsName)) {
                                    classMap.put(columnsName, new ArrayList<>());
                                }
                                field.setAccessible(true);
                                classMap.get(columnsName).add(field);
                            }
                        }
                );
                Map<Integer, List<Field>> reflectionMap = new HashMap<>(16);
                //默认读取第一个sheet
                Sheet sheet = workbook.getSheetAt(0);

                boolean firstRow = true;
                for (int i = sheet.getFirstRowNum(); i <= sheet.getLastRowNum(); i++) {
                    Row row = sheet.getRow(i);
                    //提取Excel表头
                    if (firstRow) {
                        for (int j = row.getFirstCellNum(); j <= row.getLastCellNum(); j++) {
                            Cell cell = row.getCell(j);
                            String cellValue = getCellValue(cell);
                            if (classMap.containsKey(cellValue)) {
                                reflectionMap.put(j, classMap.get(cellValue));
                            }
                        }
                        firstRow = false;
                    } else {
                        if (row == null) {
                            continue;
                        }
                        //判断是否为空白行
                        Boolean allBlank = PoiUtil.isEmptyRow(row, classMap.size());
                        if (allBlank) {
                            continue;
                        }
                        T t = cls.newInstance();
                        for (int j = 0; j < classMap.size(); j++) {
                            if (reflectionMap.containsKey(j)) {
                                Cell cell = row.getCell(j);
                                String cellValue = getCellValue(cell);
                                if (StringUtils.isNotBlank(cellValue)) {
                                    allBlank = false;
                                }
                                List<Field> fieldList = reflectionMap.get(j);
                                for (Field x : fieldList) {
                                    Excel annotation = x.getAnnotation(Excel.class);
                                    String columnName = annotation.name();
                                    try {
                                        handleField(t, cellValue, x);
                                    } catch (Exception e) {
                                        log.error(String.format("reflect field:%s value:%s exception!", columnName, cellValue), e);
                                        throw new Exception(String.format("字段:%s 转换异常 转化值为%s,位于第%s行%s列", columnName,cellValue, i + 1, j + 1));
                                    }
                                }
                            }
                        }
                        dataList.add(t);
                    }
                }
            }
        } catch (Exception e) {
            log.error(String.format("parse excel exception!"), e);
            throw new UdpBootException(e.getMessage());
        }
        return dataList;
    }


    /**
     * 读取cell的内容
     * @param cell
     * @return
     */
    private static String getCellValue(Cell cell) {
        if (cell == null) {
            return "";
        }
        if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {
            if (HSSFDateUtil.isCellDateFormatted(cell)) {
                return HSSFDateUtil.getJavaDate(cell.getNumericCellValue()).toString();
            } else {
                return new BigDecimal(cell.getNumericCellValue()).toString();
            }
        } else if (cell.getCellType() == Cell.CELL_TYPE_STRING) {
            return StringUtils.trimToEmpty(cell.getStringCellValue());
        } else if (cell.getCellType() == Cell.CELL_TYPE_FORMULA) {
            return StringUtils.trimToEmpty(cell.getCellFormula());
        } else if (cell.getCellType() == Cell.CELL_TYPE_BLANK) {
            return "";
        } else if (cell.getCellType() == Cell.CELL_TYPE_BOOLEAN) {
            return String.valueOf(cell.getBooleanCellValue());
        } else if (cell.getCellType() == Cell.CELL_TYPE_ERROR) {
            return "ERROR";
        } else {
            return cell.toString().trim();
        }
    }

    /**
     * 判断是否为空白行
     * @param row
     * @param num
     * @return
     */
    private static Boolean isEmptyRow(Row row, int num) {
        Boolean re = true;
        for (int j = 0; j < num; j++) {
            Cell cell = row.getCell(j);
            String cellValue = getCellValue(cell);
            if (StringUtils.isNotBlank(cellValue)) {
                re = false;
                break;
            }
        }
        return re;
    }

    /**
     * 写入数据到实例中
     * @param t
     * @param value
     * @param field
     * @param <T>
     * @throws Exception
     */
    private static <T> void handleField(T t, String value, Field field) throws Exception {
        Class<?> type = field.getType();
        if (type == null || type == void.class || StringUtils.isBlank(value)) {
            return;
        }
        if (type == Object.class) {
            field.set(t, value);
            //数字类型
        } else if (type.getSuperclass() == null || type.getSuperclass() == Number.class) {
            if (type == int.class || type == Integer.class) {
                field.set(t, NumberUtils.toInt(value));
            } else if (type == long.class || type == Long.class) {
                field.set(t, NumberUtils.toLong(value));
            } else if (type == byte.class || type == Byte.class) {
                field.set(t, NumberUtils.toByte(value));
            } else if (type == short.class || type == Short.class) {
                field.set(t, NumberUtils.toShort(value));
            } else if (type == double.class || type == Double.class) {
                field.set(t, NumberUtils.toDouble(value));
            } else if (type == float.class || type == Float.class) {
                field.set(t, NumberUtils.toFloat(value));
            } else if (type == char.class || type == Character.class) {
                field.set(t, CharUtils.toChar(value));
            } else if (type == boolean.class) {
                field.set(t, BooleanUtils.toBoolean(value));
            } else if (type == BigDecimal.class) {
                field.set(t, new BigDecimal(value));
            }
        } else if (type == Boolean.class) {
            field.set(t, BooleanUtils.toBoolean(value));
        } else if (type == Date.class) {
            //
            Pattern pattern = Pattern.compile("\\d{4}-\\d{2}-\\d{2}");
            Pattern compile = Pattern.compile("\\d{4}/\\d{2}/\\d{2}");
            Pattern newest = Pattern.compile("\\d{4}\\d{2}\\d{2} \\d{2}:\\d{2}:\\d{2}");
            Matcher matcher = pattern.matcher(value);
            if( matcher.matches() ){
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                field.set(t, sdf.parse(value));
            } else if (compile.matcher(value).matches()) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
                field.set(t, sdf.parse(value));
            } else if (newest.matcher(value).matches()) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
                field.set(t, sdf.parse(value));
            } else {
                SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.US);
                field.set(t, sdf.parse(value));
            }
        } else if (type == String.class) {
            field.set(t, value);
        } else {
            Constructor<?> constructor = type.getConstructor(String.class);
            field.set(t, constructor.newInstance(value));
        }
    }

    public static InputStream exportFileReturnInputStream(String url, String fileName) throws Exception {
        log.info("附件转换成流:{},{}", url, fileName);
        URL urlObject = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) urlObject.openConnection();
        if (conn instanceof HttpsURLConnection) {
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new SecureRandom());
            ((HttpsURLConnection) conn).setSSLSocketFactory(sc.getSocketFactory());
            ((HttpsURLConnection) conn).setHostnameVerifier(new TrustAnyHostnameVerifier());
        }
        conn.connect();
        return conn.getInputStream();
    }

}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值