Java Poi 在Excel中输出特殊符号

2022-08-02,,,,

最近的工作围绕报表导出,并没有集成相应的报表插件,只是使用了Poi。其中有一个需求,Excel中导出特殊符号,如√、×等。在网上找寻了许久,没有相关资料,故记录分享一下。

思考良久,走了不少弯路,最后受 System.out.println() 启发,实现方式真的超级简单。每一个特殊符号,都对应一个Unicode编码,我们只需要将特定的符号,转变成Unicode编码,进行输出即可。

相应的代码输出:

cell.setCellValue("\u221A");

另附自己编写的Excel工具类,支持单表、主子表(可定制主表在前还是在后)、图片、特殊符号等。

<dependency>
   <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.1.2</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>4.1.2</version>
</dependency>
package com.king.tools.util;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ππ
 * @date 2020-6-22 17:03
 * 导出的Excel中,百分比
 */

public class ExcelPercentField {
    public final static Map<String,String> percentFiledMap = new HashMap<>();
    static {
   		// 根据实际情况进行设置
        percentFiledMap.put("a","a");
        percentFiledMap.put("b","b");
        percentFiledMap.put("c","c");
    }
}
package com.king.tools.util;

import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.RegionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author ππ
 * @date 2020-6-10 14:45
 * excel 导出通用类
 * 采用反射生成
 * 目前仅支持导出slx,暂不支持导出xlsx格式
 */

public class ExcelExport<T> {
    Logger logger = LoggerFactory.getLogger(ExcelExport.class);
    private HSSFWorkbook workbook;
    private HSSFSheet sheet;
    private int rowNum;
    private HSSFPatriarch patriarch ;
    private String fileName;
    private int version;

    public ExcelExport(){}
    public ExcelExport(String fileName, int version) {
        this.fileName = fileName;
        this.version = version;
    }

    /**
     * 导出Excel到指定位置
     * @param fields 字段集合  主表key为entity,子表key为children
     * @param dataset 数据集合  注意:如果为主子表,主表中,子表集合对应的属性名必须为children,反射使用的children进行映射,可修改
     * @param path    文件路径
     */
    public void exportExcel(String title, Map<String,List<String>> fields, Collection<T> dataset, String path,boolean childBefore){
        createExcelHSSF(title,fields,null,dataset,DateUtils.YYYY_MM_DD,path,childBefore);
    }

    /**
     * 导出Excel到指定位置
     * @param fields 字段集合 主表key为entity,子表key为children
     * @param header 表头数组
     * @param dataset 数据集合 注意:如果为主子表,主表中,子表集合对应的属性名必须为children,反射使用的children进行映射,可修改
     * @param path    文件路径
     * @param childBefore  子表在前  默认false
     */
    public void exportExcel(String title,Map<String,List<String>> fields,String[] header,Collection<T> dataset,String path,boolean childBefore){
        createExcelHSSF(title,fields,header,dataset,DateUtils.YYYY_MM_DD,path,childBefore);
    }

    /**
     * 导出Excel到指定位置
     * @param fields  字段集合 主表key为entity,子表key为children
     * @param header  表头数组
     * @param dataset 数据集合 注意:如果为主子表,主表中,子表集合对应的属性名必须为children,反射使用的children进行映射,可修改
     * @param pattern 日期格式
     * @param path    文件路径
     * @param childBefore 子表在前
     */
    public void exportExcel(String title,Map<String,List<String>> fields,String[] header,Collection<T> dataset,String pattern,String path,boolean childBefore){
        createExcelHSSF(title,fields,header,dataset,pattern,path,childBefore);
    }

    /**
     * 导出文件到本地
     * @param fields  字段集合 主表key为entity,子表key为children
     * @param dataset  数据集合 注意:如果为主子表,主表中,子表集合对应的属性名必须为children,反射使用的children进行映射,可修改
     * @param response  http
     */
    public void exportExcel(String title,Map<String,List<String>> fields, Collection<T> dataset, HttpServletResponse response){
        createExcelHSSF(title,fields,null,dataset,DateUtils.YYYY_MM_DD,response);
    }

    /**
     * 导出文件到本地
     * @param fields  字段集合 主表key为entity,子表key为children
     * @param header  表头数组
     * @param dataset 数据集合 注意:如果为主子表,主表中,子表集合对应的属性名必须为children,反射使用的children进行映射,可修改
     * @param response http
     */
    public void exportExcel(String title, Map<String,List<String>> fields, String[] header, Collection<T> dataset, HttpServletResponse response){
        createExcelHSSF(title,fields,header,dataset,DateUtils.YYYY_MM_DD,response);
    }

    /**
     * 导出文件到本地
     * @param fields  字段集合 主表key为entity,子表key为children
     * @param header  表头数组
     * @param dataset 数据集合 注意:如果为主子表,主表中,子表集合对应的属性名必须为children,反射使用的children进行映射,可修改
     * @param pattern 日期格式
     * @param response http
     */
    public void exportExcel(String title, Map<String,List<String>> fields, String[] header, Collection<T> dataset, String pattern, HttpServletResponse response){
        createExcelHSSF(title,fields,header,dataset,pattern,response);
    }
    /**
     * 页面下载excel
     * @param title
     * @param fields
     * @param header
     * @param dataset
     * @param pattern
     * @param response
     */
    private void createExcelHSSF(String title, Map<String,List<String>> fields, String[] header, Collection<T> dataset, String pattern, HttpServletResponse response){
        response.reset(); // 清除buffer缓存
        // 指定下载的文件名
        response.setHeader("Content-Disposition", "attachment;filename=contacts" +(StringUtils.isBlank(fileName)?  DateUtils.dateTimeNow() : fileName) + ".xls");
        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        createExcel2003(title,fields,header,dataset,pattern, false);
        httpExcelHSSF(workbook,response);
    }

    /**
     * 输出到指定路径
     * @param title
     * @param fields
     * @param header
     * @param dataset
     * @param pattern
     * @param path
     * @param childBefore
     */
    private void createExcelHSSF(String title,Map<String,List<String>> fields,String[] header,Collection<T> dataset,String pattern,String path,boolean childBefore){
        createExcel2003(title,fields,header,dataset,pattern,childBefore);
        ioExcelHSSF(workbook,path);
    }

    /**
     * 公共方法,创建excel 2003版
     * @param title
     * @param fields
     * @param header
     * @param dataset
     * @param pattern
     * @param childBefore
     */
    private void createExcel2003(String title, Map<String, List<String>> fields, String[] header, Collection<T> dataset, String pattern, boolean childBefore){
        // 初始化构建
        initWorkBook();
        // 生成样式
        HSSFCellStyle titleStyle = getTitleStyle(workbook);
        HSSFCellStyle headerStyle = getHeaderStyle(workbook);
        HSSFCellStyle normalStyle = getNormalStyle(workbook);
        HSSFCellStyle footerStyle = getFooterStyle(workbook);
        HSSFCellStyle percentStyle = createPercentStyle(workbook);
        // 创建表头
        createTableTitle(title,header.length-1,titleStyle);
        // 生成标题行
        createTableHead(header,headerStyle);
        // 迭代集合
        Iterator it = dataset.iterator();
        // 获取主表属性字段
        List<String> entityFields = fields.get("entity");
        // 获取子表属性字段
        List<String> childFields = fields.get("children");
        // 主表字段长度
        int entityColumnLength = entityFields.size();
        int childColumnLength = 0;
        if(childFields !=null){
            childColumnLength = childFields.size();
        }
        // 合并行
        int rowspan = 0;
        // 每个对象的子表数据
        Object children = null;
        HSSFRow row;
        HSSFCell cell;
        while (it.hasNext()){
            rowNum ++;
            T t = (T) it.next();
            row = sheet.createRow(rowNum);
            // 确定合并行数
            if(childFields !=null && childFields.size() > 0){
                children = getValue(t,"children");
                if(children !=null && ((ArrayList)children).size()>0){
                    rowspan = ((ArrayList)children).size()-1;
                }
            }
            // 主表字段
            for(int i = 0; i <entityFields.size(); i++){
                Object value = getValue(t,entityFields.get(i));
                // 创建单元格
                if(childBefore){
                    if(ExcelPercentField.percentFiledMap.containsKey(entityFields.get(i))){
                        createTableCell(row.createCell(i+childColumnLength),value,percentStyle,pattern,rowspan);
                    }else{
                        createTableCell(row.createCell(i+childColumnLength),value,normalStyle,pattern,rowspan);
                    }
                }else{
                    if(ExcelPercentField.percentFiledMap.containsKey(entityFields.get(i))){
                        createTableCell(row.createCell(i),value,percentStyle,pattern,rowspan);
                    }else{
                        createTableCell(row.createCell(i),value,normalStyle,pattern,rowspan);
                    }
                }
            }
            // 子表字段
            if(childFields !=null && childFields.size() > 0){
                if(children !=null ){
                    List list = (ArrayList)children;
                    for(int i = 0;i <list.size(); i++){
                        if(i >0){
                            rowNum++;
                            row = sheet.createRow(rowNum);
                        }
                        for(int j = 0;j<childFields.size();j++){
                            Object value = getValue(list.get(i),childFields.get(j));
                            if(childBefore){
                                if(ExcelPercentField.percentFiledMap.containsKey(childFields.get(j))){
                                    createTableCell(row.createCell(j ),value,percentStyle,pattern,rowspan);
                                }else{
                                    createTableCell(row.createCell(j ),value,normalStyle,pattern,rowspan);
                                }
                            }else{
                                if(ExcelPercentField.percentFiledMap.containsKey(childFields.get(j))){
                                    createTableCell(row.createCell(j +entityColumnLength),value,percentStyle,pattern,rowspan);
                                }else{
                                    createTableCell(row.createCell(j +entityColumnLength),value,normalStyle,pattern,rowspan);
                                }

                            }
                        }
                    }
                }
            }
            // 如果需要合并行
            if(rowspan > 0){
               for(int i = 0;i<entityFields.size();i++){
                   CellRangeAddress cellRange = null;
                   if(childBefore){
                       cellRange= new CellRangeAddress(rowNum-rowspan,rowNum,i+childColumnLength,i+childColumnLength);
                   }else{
                       cellRange = new CellRangeAddress(rowNum-rowspan,rowNum,i,i);
                   }
                   sheet.addMergedRegion(cellRange);
                   //添加边框
                   RegionUtil.setBorderTop(BorderStyle.THIN, cellRange, sheet);
                   RegionUtil.setBorderBottom(BorderStyle.THIN, cellRange, sheet);
                   RegionUtil.setBorderLeft(BorderStyle.THIN, cellRange, sheet);
                   RegionUtil.setBorderRight(BorderStyle.THIN, cellRange, sheet);
                   RegionUtil.setTopBorderColor(HSSFColor.HSSFColorPredefined.GREEN.getIndex(),cellRange,sheet);
                   RegionUtil.setBottomBorderColor(HSSFColor.HSSFColorPredefined.GREEN.getIndex(),cellRange,sheet);
                   RegionUtil.setLeftBorderColor(HSSFColor.HSSFColorPredefined.GREEN.getIndex(),cellRange,sheet);
                   RegionUtil.setRightBorderColor(HSSFColor.HSSFColorPredefined.GREEN.getIndex(),cellRange,sheet);
               }
            }
        }
        sheet.autoSizeColumn(2);
        setSizeColumn(sheet,entityColumnLength+childColumnLength);
    }
    /**
     * 初始化构建工作簿
     */
    private void initWorkBook(){
        // 创建一个工作簿
        workbook = HSSFWorkbookFactory.createWorkbook();
        // 创建一个sheet
        sheet = workbook.createSheet();
        // 默认表格列宽
        sheet.setDefaultColumnWidth(18);
        patriarch = sheet.createDrawingPatriarch();
    }
    /**
     * 创建Excel标题
     * @param title 标题
     * @param colspan 合并列
     * @param headerStyle 样式
     */
    private void createTableTitle(String title,int colspan, HSSFCellStyle headerStyle) {
        if(StringUtils.isBlank(title)){
            return;
        }
        HSSFRow row = sheet.createRow(rowNum);
        row.setHeightInPoints(30f);
        HSSFCell cell = row.createCell(0);
        sheet.addMergedRegion(new CellRangeAddress(rowNum,rowNum,0,colspan));
        cell.setCellStyle(headerStyle);
        cell.setCellValue(title);
        rowNum ++;
    }
    /**
     * 创建Excel表头
     * @param header
     * @param headerStyle
     */
    private void createTableHead(String[] header, HSSFCellStyle headerStyle) {
        if(header ==null || header.length <1){
            return;
        }
        HSSFRow row = sheet.createRow(rowNum);
        HSSFCell cell;
        for (int i = 0; i < header.length; i++){
            cell = row.createCell(i);
            cell.setCellStyle(headerStyle);
            cell.setCellValue(header[i]);
            cell.setCellType(CellType.STRING);
        }
    }

    /**
     * 创建单元格
     * @param cell
     * @param value
     * @param normalStyle
     */
    private void createTableCell(HSSFCell cell, Object value, HSSFCellStyle normalStyle, String pattern, int rowspan) {
        cell.setCellStyle(normalStyle);
        if (value ==null){
            return;
        }
        if(value instanceof Number){
            cell.setCellType(CellType.NUMERIC);
            cell.setCellValue(Double.parseDouble(value.toString()));
        //日期
        } else if(value instanceof Date){
            cell.setCellType(CellType.STRING);
            cell.setCellValue(DateUtils.parseDateToStr(pattern,(Date)value));
        // 图片
        } else if(value instanceof byte[]){
            cell.getRow().setHeightInPoints(80);
            sheet.setColumnWidth(cell.getColumnIndex(),(short) (34.5 * 110));
            HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0,
                    1023, 255, (short) cell.getColumnIndex(), rowNum, (short) cell.getColumnIndex(), rowNum);
            anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_DONT_RESIZE);
            patriarch.createPicture(anchor, workbook.addPicture(
                    (byte[])value, HSSFWorkbook.PICTURE_TYPE_JPEG));

        }else if(value instanceof Boolean){
            cell.setCellType(CellType.STRING);
            if((boolean)value){
                cell.setCellValue("\u221A");
            }
            // 全部当作字符串处理
        }else{
            cell.setCellType(CellType.STRING);
            cell.setCellValue(new HSSFRichTextString(String.valueOf(value)));
        }
    }

    /**
     * 创建标题行
     * @param workbook
     * @return
     */
    private HSSFCellStyle getTitleStyle(HSSFWorkbook workbook) {
        HSSFCellStyle style = getNormalStyle(workbook);
        style.getFont(workbook).setFontHeightInPoints((short)12);
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        return style;
    }

    /**
     * 创建尾部合计行
     * @param workbook
     * @return
     */
    private HSSFCellStyle getFooterStyle(HSSFWorkbook workbook) {
        HSSFCellStyle style = getNormalStyle(workbook);
        style.getFont(workbook).setFontHeightInPoints((short)12);
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setFillForegroundColor(IndexedColors.LIME.getIndex());
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        return style;
    }

    /**
     * 创建表头样式
     * @param workbook
     * @return
     */
    private HSSFCellStyle getHeaderStyle(HSSFWorkbook workbook) {
        HSSFCellStyle style = getNormalStyle(workbook);
        style.getFont(workbook).setFontHeightInPoints((short)11);
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setFillForegroundColor(IndexedColors.LIME.getIndex());
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        HSSFPalette palette = workbook.getCustomPalette();
        palette.setColorAtIndex(IndexedColors.LIME.getIndex(),(byte)198,(byte)224,(byte)180);
        return style;
    }

    /**
     * 百分比格式
     * @param workbook
     * @return
     */
    private HSSFCellStyle createPercentStyle(HSSFWorkbook workbook){
        HSSFCellStyle style = getNormalStyle(workbook);
        style.setDataFormat(HSSFDataFormat.getBuiltinFormat("0.00%"));
        return style;
    }

    /**
     * 创建普通样式
     * @param workbook
     * @return
     */
    private HSSFCellStyle getNormalStyle(HSSFWorkbook workbook){
        // 创建字体
        HSSFFont font = workbook.createFont();
        font.setFontHeightInPoints((short)10);
        // 构建样式
        HSSFCellStyle style = workbook.createCellStyle();
        // 设置边框
        style.setBorderTop(BorderStyle.THIN);
        style.setBorderRight(BorderStyle.THIN);
        style.setBorderBottom(BorderStyle.THIN);
        style.setBorderLeft(BorderStyle.THIN);
        style.setTopBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        style.setRightBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        style.setBottomBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        style.setLeftBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setFont(font);
        // 字体默认换行
        style.setWrapText(true);
        return style;
    }


    /**
     * 反射获取值
     * @param t
     * @param fieldName
     * @param <E>
     * @return
     */
    private <E> Object  getValue(E t,String fieldName){
        String methodName = "get"
                + fieldName.substring(0, 1).toUpperCase()
                + fieldName.substring(1);
        try {
            Method method = t.getClass().getMethod(methodName);
            method.setAccessible(true);
            Object value = method.invoke(t);
            return value;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 输出IO流
     * @param workbook
     * @param path
     * @return
     */
    private void ioExcelHSSF(HSSFWorkbook workbook, String path){
        OutputStream ops =null;
        if(StringUtils.isBlank(fileName)){
            path = path + DateUtils.dateTimeNow() +".xls";
        } else {
            path = path + fileName + ".xls";
        }
        try {
            ops = new FileOutputStream(path);
            workbook.write(ops);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(ops != null){
                try {
                    ops.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void httpExcelHSSF(HSSFWorkbook workbook, HttpServletResponse response){
        OutputStream ops = null;
        try {
            ops = response.getOutputStream();
            response.flushBuffer();
            workbook.write(ops);
        } catch (IOException e) {
            e.printStackTrace();
            if(ops !=null){
                try {
                    ops.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
    }

    /**
     * 自适应列宽
     * @param sheet
     * @param size 列数
     */
    private void setSizeColumn(HSSFSheet sheet, int size) {
        for(int i =0;i<size;i++){
            int columnWidth = sheet.getColumnWidth(i) / 256;
            for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) {
                HSSFRow currentRow;
                //当前行未被使用过
                if (sheet.getRow(rowNum) == null) {
                    currentRow = sheet.createRow(rowNum);
                } else {
                    currentRow = sheet.getRow(rowNum);
                }

                if (currentRow.getCell(i) != null) {
                    HSSFCell currentCell = currentRow.getCell(i);
//                    if(rowNum==sheet.getLastRowNum()){
//                        HSSFCellStyle style = currentCell.getCellStyle();
//                        style.setFillForegroundColor(IndexedColors.LIME.getIndex());
//                        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
//                        currentCell.setCellStyle(style);
//                    }
                    if (currentCell.getCellType() == CellType.STRING) {
                        int length = currentCell.getStringCellValue().getBytes().length;
                        if (columnWidth < length) {
                            columnWidth = length;
                        }
                    }
                }
            }
            sheet.setColumnWidth(i, columnWidth * 256);
        }
    }
}

效果图如下:

但仍遇到一个问题,主子表结构导出,如果图片在主表,合并行之后,图片并不会居中,并且第一行会被撑开,有没有比较简单的方式进行处理(不想重新计算锚点,然后定高输出)?

本文地址:https://blog.csdn.net/qq_33811736/article/details/107377195

《Java Poi 在Excel中输出特殊符号.doc》

下载本文的Word格式文档,以方便收藏与打印。