• 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
            );

    演示

     

    作者:陈彦斌

    个性签名:没有学不会的技术,只有不学习的人!
    联系方式:543210188(WeChat/QQ),推荐WeChat
  • 相关阅读:
    Mysql.linux登录数据库
    Linux.vim编辑器显示行号
    经济学-泰勒
    MyBatis.多条件排序
    Mysql.复选条件的查询
    前端.省市级联框
    前端.解决form-contral总是换行问题
    若依框架. 仿ThymeLeaf前端SelectDictLable方法
    excel中为什么不显示单引号
    python-爬虫(3)---lxml匹配css
  • 原文地址:https://www.cnblogs.com/chenyanbin/p/easypoiexcel.html
Copyright © 2020-2023  润新知