All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.creekmoon.excelUtils.core.ExcelExport Maven / Gradle / Ivy

There is a newer version: 1.3.0
Show newest version
package cn.creekmoon.excelUtils.core;


import cn.creekmoon.excelUtils.threadPool.CleanTempFilesExecutor;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.poi.excel.BigExcelWriter;
import cn.hutool.poi.excel.ExcelUtil;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

/**
 * @author JY
 * @date 2022-01-05
 */
@Slf4j
public class ExcelExport {
    /**
     * 打印调试内容
     */
    protected boolean debugger = false;
    /*唯一识别名称*/
    public String taskId = UUID.fastUUID().toString();
    /*自定义的名称*/
    public String excelName;
    /*写入器*/
    private BigExcelWriter bigExcelWriter;
    /*
     * sheet页和导出对象的映射关系
     * */
    private Map sheetName2SheetWriter = new HashMap<>();

    private ExcelExport() {

    }

    public static  SheetWriter create(Class c) {
        ExcelExport excelExport = create();
        return excelExport.switchSheet(c);
    }

    public static ExcelExport create() {
        ExcelExport excelExport = new ExcelExport();
        excelExport.excelName = ExcelConstants.excelNameGenerator.get();
        return excelExport;
    }

    /**
     * 生成sheetName
     *
     * @param sheetIndex sheet页下标
     * @return
     */
    @NotNull
    protected static String generateSheetNameByIndex(Integer sheetIndex) {
        return "Sheet" + (sheetIndex + 1);
    }

    /**
     * 切换到新的标签页
     */
    public  SheetWriter switchSheet(String sheetName, Class newDataClass) {
        /*第一次切换sheet页是重命名当前sheet*/
        if (sheetName2SheetWriter.isEmpty()) {
            getBigExcelWriter().renameSheet(sheetName);
        }
        /*后续切换sheet页是新增sheet*/
        if (!sheetName2SheetWriter.isEmpty()) {
            getBigExcelWriter().setSheet(sheetName);
        }
        return sheetName2SheetWriter.computeIfAbsent(sheetName, s -> {
            return new SheetWriter<>(this, new SheetWriterContext(sheetName));
        });

    }

    /**
     * 切换到新的标签页
     */
    public  SheetWriter switchSheet(Class newDataClass) {
        int indexSeq = 0;
        while (sheetName2SheetWriter.containsKey(generateSheetNameByIndex(indexSeq))) {
            indexSeq++;
        }
        return switchSheet(generateSheetNameByIndex(indexSeq), newDataClass);
    }

    public ExcelExport debug() {
        this.debugger = true;
        return this;
    }


    public static void cleanTempFileDelay(String taskId) {
        CleanTempFilesExecutor.cleanTempFileDelay(taskId);
    }

    public void cleanTempFileDelay() {
        CleanTempFilesExecutor.cleanTempFileDelay(taskId);
    }

    /**
     * 停止写入
     *
     * @return taskId
     */
    public String stopWrite() {
        getBigExcelWriter().close();
        return taskId;
    }


    /**
     * 响应请求 返回结果
     *
     * @param taskId
     * @param responseExcelName
     * @param response
     * @throws IOException
     */
    public static void response(String taskId, String responseExcelName, HttpServletResponse response) throws IOException {
        try {
            if (response != null) {
                responseByFilePath(PathFinder.getAbsoluteFilePath(taskId), responseExcelName, response);
            }
        } finally {
            cleanTempFileDelay(taskId);
        }
    }


    /**
     * 返回Excel
     *
     * @param filePath          本地文件路径
     * @param responseExcelName 声明的文件名称,前端能看到 可以自己乱填
     * @param response          servlet请求
     * @throws IOException
     */
    /*回应请求*/
    private static void responseByFilePath(String filePath, String responseExcelName, HttpServletResponse response) throws IOException {
        /*现代浏览器标准, RFC5987标准协议 显式指定文件名的编码格式为UTF-8 但是这样swagger-ui不支持回显 比较坑*/
//        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
//        response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + URLEncoder.encode(responseExcelName + ".xlsx", "UTF-8"));

        /*旧版协议 不能使用中文文件名 但是兼容所有浏览器*/
//        response.setContentType("application/octet-stream");
//        response.setHeader("Content-Disposition", "attachment;filename=" + responseExcelName + ".xlsx");

        /*当前的兼容做法, 使用旧版协议, 但是用UTF-8编码文件名. 这样一来支持旧版浏览器下载文件(但文件名乱码), 同时现代浏览器能下载也能会自动解析成中文文件名*/
        response.setContentType("application/octet-stream");
        responseExcelName = URLEncoder.encode(responseExcelName + ".xlsx", "UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + responseExcelName);

        /*使用流将文件传输回去*/
        ServletOutputStream out = null;
        InputStream fileInputStream = null;
        try {
            out = response.getOutputStream();
            fileInputStream = FileUtil.getInputStream(filePath);
            byte[] b = new byte[4096];  //创建数据缓冲区  通常网络2-8K 磁盘32K-64K
            int length;
            while ((length = fileInputStream.read(b)) > 0) {
                out.write(b, 0, length);
            }
            out.flush();
            out.close();
        } finally {
            IoUtil.close(fileInputStream);
            IoUtil.close(out);
        }

    }

    /**
     * 响应并清除文件
     *
     * @param response
     * @throws IOException
     */
    public void response(HttpServletResponse response) throws IOException {
        String taskId = this.stopWrite();
        ExcelExport.response(taskId, excelName, response);
    }


    /**
     * 内部操作类,但是暴露出来了,希望最好不要用这个方法
     *
     * @return
     */
    public BigExcelWriter getBigExcelWriter() {
        if (bigExcelWriter == null) {
            bigExcelWriter = ExcelUtil.getBigWriter(PathFinder.getAbsoluteFilePath(taskId));
        }
        return bigExcelWriter;
    }

    /**
     * 写入策略
     */
    public enum WriteStrategy {
        /*忽略取值异常 通常是多级属性空指针造成的 如果取不到值直接置为NULL*/
        CONTINUE_ON_ERROR,
        /*遇到任何失败的情况则停止*/
        STOP_ON_ERROR;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy