SpringBoot整合EasyPoi 封裝Excel導出通用工具類,行高自適應,導出圖片

導讀

  下午抽空封裝一個通用導出Excel工具類。之前還寫過一篇EasyPoi導入參數校驗,批註導出,點我直達

添加依賴

 <!-- easy poi -->
            <dependency>
                <groupId>cn.afterturn</groupId>
                <artifactId>easypoi-spring-boot-starter</artifactId>
                <version>4.1.0</version>
            </dependency>

注意依賴版本超過4.1.0導出圖片時有問題喲

EasyPoi工具類

時間工具類

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;

/**
 * 時間工具類
 *
 * @Author:chenyanbin
 */
public class TimeUtil {
    public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";

    public static final String YYYY_MM_DD = "yyyy-MM-dd";

    public static final String YYMMDD = "yyMMdd";

    public static final String YYYYMMDD = "yyyyMMdd";

    /**
     * 格式化日期
     *
     * @param time 當前時間
     * @return
     */
    public static String format(Date time) {
        return format(time, YYYY_MM_DD_HH_MM_SS);
    }

    /**
     * 格式化日期
     *
     * @param time    當前時間
     * @param pattern 格式化規則
     * @return
     */
    public static String format(Date time, String pattern) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern);
        ZoneId zoneId = ZoneId.systemDefault();
        return dtf.format(time.toInstant().atZone(zoneId));
    }

    /**
     * timestamp 轉 字符串
     *
     * @param timestamp
     * @return
     */
    public static String dateToStr(long timestamp) {
        return dateToStr(timestamp, YYYY_MM_DD_HH_MM_SS);
    }

    /**
     * timestamp 轉 字符串
     *
     * @param timestamp 時間戳
     * @param pattern   格式化規則
     * @return
     */
    public static String dateToStr(long timestamp, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        ZoneId zoneId = ZoneId.systemDefault();
        String timeStr = formatter.format(new Date(timestamp).toInstant().atZone(zoneId));
        return timeStr;
    }

    /**
     * 字符串 轉 date
     *
     * @param time
     * @return
     */
    public static Date strToDate(String time) {
        return strToDate(time, YYYY_MM_DD_HH_MM_SS);
    }

    /**
     * 字符串 轉 date
     *
     * @param time    當前時間
     * @param pattern 格式化規則
     * @return
     */
    public static Date strToDate(String time, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        LocalDateTime localDateTime = LocalDateTime.parse(time, formatter);
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 當前時間格式化
     *
     * @return
     */
    public static String currentTimeFormat() {
        LocalDateTime nowDate = LocalDateTime.now();
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS);
        return dtf.format(nowDate);
    }

    /**
     * 當前時間格式化
     *
     * @param pattern 格式化規則
     * @return
     */
    public static String currentTimeFormat(String pattern) {
        LocalDateTime nowDate = LocalDateTime.now();
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern);
        return dtf.format(nowDate);
    }
}

導出樣式

import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.entity.params.ExcelForEachParams;
import cn.afterturn.easypoi.excel.export.styler.IExcelExportStyler;
import org.apache.poi.ss.usermodel.*;

/**
 * Easy Poi 導出樣式
 * @Author:chenyanbin
 */
public class EasyPoiExcelExportStylerUitl implements IExcelExportStyler {

    private static final short STRING_FORMAT = (short) BuiltinFormats.getBuiltinFormat("TEXT");
    private static final short FONT_SIZE_TEN = 10;
    private static final short FONT_SIZE_ELEVEN = 11;
    private static final short FONT_SIZE_TWELVE = 12;
    /**
     * 大標題樣式
     */
    private CellStyle headerStyle;
    /**
     * 每列標題樣式
     */
    private CellStyle titleStyle;
    /**
     * 數據行樣式
     */
    private CellStyle styles;

    public EasyPoiExcelExportStylerUitl(Workbook workbook) {
        this.init(workbook);
    }

    /**
     * 初始化樣式
     *
     * @param workbook
     */
    private void init(Workbook workbook) {
        this.headerStyle = initHeaderStyle(workbook);
        this.titleStyle = initTitleStyle(workbook);
        this.styles = initStyles(workbook);
    }

    /**
     * 大標題樣式
     *
     * @param color
     * @return
     */
    @Override
    public CellStyle getHeaderStyle(short color) {
        return headerStyle;
    }

    /**
     * 每列標題樣式
     *
     * @param color
     * @return
     */
    @Override
    public CellStyle getTitleStyle(short color) {
        return titleStyle;
    }

    /**
     * 數據行樣式
     *
     * @param parity 可以用來表示奇偶行
     * @param entity 數據內容
     * @return 樣式
     */
    @Override
    public CellStyle getStyles(boolean parity, ExcelExportEntity entity) {
        return styles;
    }

    /**
     * 獲取樣式方法
     *
     * @param dataRow 數據行
     * @param obj     對象
     * @param data    數據
     */
    @Override
    public CellStyle getStyles(Cell cell, int dataRow, ExcelExportEntity entity, Object obj, Object data) {
        return getStyles(true, entity);
    }

    /**
     * 模板使用的樣式設置
     */
    @Override
    public CellStyle getTemplateStyles(boolean isSingle, ExcelForEachParams excelForEachParams) {
        return null;
    }

    /**
     * 初始化--大標題樣式
     *
     * @param workbook
     * @return
     */
    private CellStyle initHeaderStyle(Workbook workbook) {
        CellStyle style = getBaseCellStyle(workbook);
        style.setFont(getFont(workbook, (short) 14, true));
        return style;
    }

    /**
     * 初始化--每列標題樣式
     *
     * @param workbook
     * @return
     */
    private CellStyle initTitleStyle(Workbook workbook) {
        CellStyle style = getBaseCellStyle(workbook);
        style.setFont(getFont(workbook, FONT_SIZE_TWELVE, true));
        //背景色
//        style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
//        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        style.setDataFormat(STRING_FORMAT);
        return style;
    }

    /**
     * 初始化--數據行樣式
     *
     * @param workbook
     * @return
     */
    private CellStyle initStyles(Workbook workbook) {
        CellStyle style = getBaseCellStyle(workbook);
        style.setFont(getFont(workbook, FONT_SIZE_ELEVEN, false));
        //背景色
//        style.setFillForegroundColor(IndexedColors.RED.getIndex());
//        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        style.setDataFormat(STRING_FORMAT);
        return style;
    }

    /**
     * 基礎樣式
     *
     * @return
     */
    private CellStyle getBaseCellStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        //下邊框
        style.setBorderBottom(BorderStyle.THIN);
        //左邊框
        style.setBorderLeft(BorderStyle.THIN);
        //上邊框
        style.setBorderTop(BorderStyle.THIN);
        //右邊框
        style.setBorderRight(BorderStyle.THIN);
        //水平居中
        style.setAlignment(HorizontalAlignment.CENTER);
        //上下居中
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        //設置自動換行
        style.setWrapText(true);
        return style;
    }

    /**
     * 字體樣式
     *
     * @param size   字體大小
     * @param isBold 是否加粗
     * @return
     */
    private Font getFont(Workbook workbook, short size, boolean isBold) {
        Font font = workbook.createFont();
        //字體樣式
        font.setFontName("宋體");
        //是否加粗
        font.setBold(isBold);
        //字體大小
        font.setFontHeightInPoints(size);
        return font;
    }
}

導出工具類

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * EasyPoi 導出工具類
 *
 * @Author:chenyanbin
 */
@Slf4j
public class EasyPoiExportUtil {
    private static final Long KEEP_ALIVE_TIME = 60L;
    private static final int APS = Runtime.getRuntime().availableProcessors();
    private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(APS * 2, APS * 4, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new LinkedBlockingDeque<>());

    /**
     * 導出Excel 一對多關係
     *
     * @param list        Excel數據集合
     * @param title       Excel第一行標題,如果設置爲null,默認不顯示
     * @param sheetName   sheet名稱
     * @param pojoClass   泛型List的對象
     * @param fileName    導出文件名
     * @param type        excel類型 HSSF || XSSF
     * @param isOneToMany 是否一對多
     * @param response    響應體
     */
    public static void exportOneToManyExcel(
            List<?> list,
            String title,
            String sheetName,
            Class<?> pojoClass,
            String fileName,
            ExcelType type,
            boolean isOneToMany,
            HttpServletResponse response
    ) {
        ExportParams exportParams = new ExportParams(title, sheetName, type);
        exportParams.setStyle(EasyPoiExcelExportStylerUitl.class);
        AtomicReference<Workbook> workbook = new AtomicReference<>();
        workbookHandler(workbook, exportParams, pojoClass, list);
        if (workbook.get() == null) {
            return;
        }
        //判斷是否是一對多
        if (isOneToMany) {
            setRowHeight(workbook.get());
        }
        downLoadExcel(fileName, response, workbook.get());
    }

    /**
     * 導出excel
     *
     * @param list         Excel數據集合
     * @param title        Excel第一行標題,如果設置爲null,默認不顯示
     * @param sheetName    sheet名稱
     * @param pojoClass    泛型List的對象
     * @param fileName     導出文件名
     * @param setRowHeight 是否行高自適應
     * @param response     響應體
     */
    public static void exportOneExcel(
            List<?> list,
            String title,
            String sheetName,
            Class<?> pojoClass,
            String fileName,
            boolean setRowHeight,
            HttpServletResponse response
    ) {
        ExportParams exportParams = new ExportParams(title, sheetName);
        exportParams.setStyle(EasyPoiExcelExportStylerUitl.class);
        AtomicReference<Workbook> workbook = new AtomicReference<>();
        workbookHandler(workbook, exportParams, pojoClass, list);
        if (workbook.get() == null) {
            return;
        }
        //判斷是否根據內容自適應行高
        if (setRowHeight) {
            Sheet sheet = workbook.get().getSheetAt(0);
            for (int i = 0; i <= sheet.getLastRowNum(); i++) {
                Row row = sheet.getRow(i);
                setRowHeight(row);
            }
        }
        downLoadExcel(fileName, response, workbook.get());
    }

    /**
     * 下載Excel
     *
     * @param fileName
     * @param response
     * @param workbook
     */
    private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) {
        ServletOutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Pragma", "No-cache");
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            workbook.write(outputStream);
            outputStream.flush();
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    log.error("excel導出關閉輸出流異常:{}", e.getMessage());
                }
            }
        }
    }

    /**
     * 一對多,設置行高
     */
    private static void setRowHeight(Workbook workbook) {
        Sheet sheet = workbook.getSheetAt(0);
        //設置第4列的列寬爲60(下標從0開始),TestExportSub2Vo 不知道爲什麼設置了列寬但是不起作用,只能在這裏單獨設置
        sheet.setColumnWidth(3, 60 * 256);
        for (int i = 0; i <= sheet.getLastRowNum(); i++) {
            Row row = sheet.getRow(i);
            if (i == 0) {
                //設置第一行的行高(表格標題)
                row.setHeightInPoints(35);
            } else if (i == 1) {
                //設置第二行的行高(表格表頭)
                row.setHeightInPoints(25);
            } else {
                //設置其他行的行高根據內容自適應
                setRowHeight(row);
            }
        }
    }

    private static void setRowHeight(Row row) {
        //根據內容長度設置行高
        int enterCnt = 0;
        for (int j = 0; j < row.getPhysicalNumberOfCells(); j++) {
            int rwsTemp = row.getCell(j).toString().length();
            //這裏取每一行中的每一列字符長度最大的那一列的字符
            if (rwsTemp > enterCnt) {
                enterCnt = rwsTemp;
            }
        }
        //設置默認行高爲35
        row.setHeightInPoints(35);
        //如果字符長度大於35,判斷大了多少倍,根據倍數來設置相應的行高
        if (enterCnt > 35) {
            float d = enterCnt / 35;
            float f = 35 * d;
            /*if (d>2 && d<4){
                f = 35*2;
            }else if(d>=4 && d<6){
                f = 35*3;
            }else if (d>=6 && d<8){
                f = 35*4;
            }*/
            row.setHeightInPoints(f);
        }
    }

    private static void workbookHandler(AtomicReference<Workbook> workbook, ExportParams exportParams, Class<?> pojoClass, List<?> list) {
        CountDownLatch latch = new CountDownLatch(1);
        THREAD_POOL_EXECUTOR.execute(() -> {
            try {
                workbook.set(ExcelExportUtil.exportExcel(exportParams, pojoClass, list));
            } finally {
                latch.countDown();
            }
        });
        try {
            latch.await();
        } catch (InterruptedException e) {
            log.error("多線程導出等待異常:{}", e.getMessage());
        }
    }
}

根據url下載圖片轉字節數組

import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * Http工具類
 *
 * @Author:chenyanbin
 */
@Slf4j
public class HttpUtil {

    /**
     * 獲取網絡圖片轉成字節流
     *
     * @param strUrl 完整圖片地址
     * @return 圖片資源數組
     */
    public static byte[] getNetImgByUrl(String strUrl) {
        try {
            URL url = new URL(strUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(2 * 1000);
            // 通過輸入流獲取圖片數據
            InputStream inStream = conn.getInputStream();
            // 得到圖片的二進制數據
            byte[] btImg = readInputStream(inStream);
            return btImg;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 從輸入流中獲取字節流數據
     *
     * @param inStream 輸入流
     * @return 圖片流
     */
    private static byte[] readInputStream(InputStream inStream) throws Exception {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        // 設置每次讀取緩存區大小
        byte[] buffer = new byte[1024 * 10];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outStream.write(buffer, 0, len);
        }
        inStream.close();
        return outStream.toByteArray();
    }
}

導出實體類vo

import cn.afterturn.easypoi.excel.annotation.Excel;
import io.swagger.annotations.ApiModel;
import lombok.Data;

/**
 * @Author:chenyanbin
 */
@Data
@ApiModel(value = "ProductFilingExportExcelVo對象", description = "商品備案明細導出信息")
public class ProductFilingExportExcelVo {
    /**
     * 主鍵
     */
    private Long id;

    @Excel(name = "商品編碼", width = 20)
    private String productCode;

    @Excel(name = "HS編碼", width = 25)
    private String hsCode;

    @Excel(name = "商品名稱", width = 40)
    private String productName;

    @Excel(name = "品牌")
    private String brandName;

    @Excel(name = "規格型號", width = 20)
    private String specificationModel;

    @Excel(name = "申報要素", width = 40)
    private String reportElements;

    @Excel(name = "產品成分", width = 50)
    private String productComposition;

    @Excel(name = "生產企業", width = 25)
    private String enterprise;

    @Excel(name = "生產國/地區", width = 20)
    private String country;

    @Excel(name = "適用標準", width = 20)
    private String standards;

    @Excel(name = "商品條碼", width = 30)
    private String productBarCode;

    @Excel(name = "功能", width = 40)
    private String function;

    @Excel(name = "其他說明", width = 40)
    private String remark;

    @Excel(name = "商品正面", type = 2, imageType = 2)
    private byte[] productFrontPic;

    /**
     * 臨時商品正面url
     */
    private String tempProductFrontUrl;

    @Excel(name = "商品背面", type = 2, imageType = 2)
    private byte[] productBackPic;

    /**
     * 臨時商品背面url
     */
    private String tempProductBackUrl;

    @Excel(name = "商品其他面", width = 15, type = 2, imageType = 2)
    private byte[] productOtherPic;

    /**
     * 臨時商品其他url
     */
    private String tempProductOtherUrl;
}

dao查詢sql語句

    <resultMap id="exportExcelMap" type="ProductFilingExportExcelVo">
        <id column="id" property="id"/>
        <result column="product_code" property="productCode"/>
        <result column="hs_code" property="hsCode"/>
        <result column="product_name" property="productName"/>
        <result column="brand_name" property="brandName"/>
        <result column="specification_model" property="specificationModel"/>
        <result column="report_elements" property="reportElements"/>
        <result column="product_composition" property="productComposition"/>
        <result column="enterprise" property="enterprise"/>
        <result column="country" property="country"/>
        <result column="standards" property="standards"/>
        <result column="product_bar_code" property="productBarCode"/>
        <result column="function" property="function"/>
        <result column="remark" property="remark"/>
        <result column="temp_product_front_url" property="tempProductFrontUrl"/>
        <result column="temp_product_back_url" property="tempProductBackUrl"/>
        <result column="temp_product_other_url" property="tempProductOtherUrl"/>
    </resultMap>


    <!-- excel導出 -->
    <select id="exportExcel" resultMap="exportExcelMap">
        SELECT
            bpfd.id,
            bpfd.product_code,
            bpfd.hs_code,
            bpfd.product_name,
            bb.brand_name,
            bpfd.specification_model,
            bpfd.report_elements,
            bpfd.product_composition,
            bpfd.enterprise,
            bpfd.country,
            bpfd.standards,
            bpfd.product_bar_code,
            bpfd.`function`,
            bpfd.remark,
            bof.url_path temp_product_front_url,
            bob.url_path temp_product_back_url,
            boo.url_path temp_product_other_url
        FROM
            basic_product_filing_detail bpfd
            LEFT JOIN basic_brand bb ON bb.id = bpfd.brand_id
            LEFT JOIN basic_oss bof ON ( bof.id = bpfd.product_front_id AND bof.del_flag = 0 )
            LEFT JOIN basic_oss bob ON ( bob.id = bpfd.product_back_id AND bob.del_flag = 0 )
            LEFT JOIN basic_oss boo ON ( boo.id = bpfd.product_other_id AND boo.del_flag = 0 )
        WHERE
            bpfd.product_filing_id = #{filing_id}
        ORDER BY
            bpfd.id DESC
    </select>

controller層

    @ApiOperation("導出")
    @GetMapping("export_detail_excel")
    public void exportDetailExcel(
            HttpServletResponse response,
            @ApiParam(value = "主表主鍵id", required = true) @RequestParam(value = "id") Long id
    ) {
        cacheService.exportDetailExcel(response, id);
    }

service層

import javax.servlet.http.HttpServletResponse;

public interface ProductFilingCacheService {
    /**
     * 導出
     * @param response 響應體
     * @param id 主表主鍵id
     */
    void exportDetailExcel(HttpServletResponse response, Long id);
}
    service實現類!!!!
    private static final Long KEEP_ALIVE_TIME = 60L;
    private static final int APS = Runtime.getRuntime().availableProcessors();
    private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(APS * 2, APS * 4, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new LinkedBlockingDeque<>());




    @Override
    public void exportDetailExcel(HttpServletResponse response, Long id) {
        List<ProductFilingExportExcelVo> excelList = filingDetailMapper.exportExcel(id);
        CountDownLatch latch = new CountDownLatch(excelList.size());
        //轉換圖片字節碼
        excelList.forEach(obj -> {
            try {
                if (StringUtils.isNotBlank(obj.getTempProductFrontUrl())) {
                    obj.setProductFrontPic(HttpUtil.getNetImgByUrl(obj.getTempProductFrontUrl()));
                }
                if (StringUtils.isNotBlank(obj.getTempProductBackUrl())) {
                    obj.setProductBackPic(HttpUtil.getNetImgByUrl(obj.getTempProductBackUrl()));
                }
                if (StringUtils.isNotBlank(obj.getTempProductOtherUrl())) {
                    obj.setProductOtherPic(HttpUtil.getNetImgByUrl(obj.getTempProductOtherUrl()));
                }
            } finally {
                latch.countDown();
            }
        });
        try {
            latch.await();
        } catch (InterruptedException e) {
            log.error("導出Excel等待下載文件流異常:{}", e.getMessage());
        }
        //獲取貨主名稱
        String cargoOwnerName = "";
        ProductFilingDO productFilingDO = filingMapper.selectById(id);
        if (productFilingDO != null) {
            Long createUserId = productFilingDO.getCreateUserId();
            JsonData<String> jsonData = userControllerFeign.getCargoOwnerNameById(createUserId);
            if (JsonData.isSuccess(jsonData)) {
                cargoOwnerName = jsonData.getData();
            }
        }
        String fileName = cargoOwnerName + "-" + TimeUtil.currentTimeFormat(TimeUtil.YYMMDD) + ".xls";
        //導出
        EasyPoiExportUtil.exportOneExcel(
                excelList,
                fileName.replace(".xls", ""),
                "商品備案明細",
                ProductFilingExportExcelVo.class,
                fileName,
                false,
                response
        );
    }
如果需要設置導出行高自適應,使用如下導出!!!!! false改成true


//導出
        EasyPoiExportUtil.exportOneExcel(
                excelList,
                fileName.replace(".xls", ""),
                "商品備案明細",
                ProductFilingExportExcelVo.class,
                fileName,
                true,
                response
        );

演示

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章