Java导出无规则excel,如报销单,申请单,结算单等

本文介绍了一种用于处理复杂Excel结构导出的工具,通过自定义ZlCell、ZlRow和ZlSheet类实现了对单元格内容、样式、合并等的灵活控制。示例代码展示了如何创建包含多个合并单元格、图片和丰富样式的结算单模板,适用于处理需要定制化格式的Excel文件输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

普通业务我们导出的excel文件大多是有规则的表记录导出,即表头+表记录。但是如果业务上让我们去导出一些复杂的excel结构,比如导出结算单,申请单这些,如下:

类似这种excel文件格式的导出,现有的框架并不能很友好的支持,我们需要些一堆复杂的代码,而且还不能复用。

针对这种业务本人编写了以下的excel处理工具,希望可以帮助到大家。

ZlExcel类

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

/**
 * cell
 */
public class ZlCell {

    /**
     * 单元格ID
     */
    private String cellNo;

    private int cellIndex;

    /**
     * 行
     */
    private ZlRow row;
    //单元格格式
    private CellStyle style;
    //单元格
    private Cell cell ;
    //工作簿
    private XSSFWorkbook wb;

    private ZlSheet sheet;
    /**
     * 字体
     */
    private Font font;

    private Object content;

    /**
     * 背景色
     */
    private IndexedColors background;

    /**
     * 字体颜色
     */
    private IndexedColors fontColor;

    private BorderStyle borderTop;

    private BorderStyle borderLeft;

    private BorderStyle borderBottom;

    private BorderStyle borderRight;

    public ZlCell(XSSFWorkbook wb,ZlSheet sheet,ZlRow row,Integer index,int width,String url){
        this.wb = wb;
        this.sheet = sheet;
        this.row = row;
        this.cell = row.getRow().createCell(index);
        this.cellIndex = index;
        //宽度
        if(width > 0){
            setColumnWidth(width);
        }
        //设置图片
        addImage(url);
    }

    public ZlCell(XSSFWorkbook wb,ZlSheet sheet,ZlRow row,Integer index,Object content){
        this(wb,sheet,row,index,content,false,0,(short)11);
    }

    public ZlCell(XSSFWorkbook wb,ZlSheet sheet,ZlRow row,Integer index,Object content,boolean hold){
        this(wb,sheet,row,index,content,hold,0,(short)11);
    }


    public ZlCell(XSSFWorkbook wb,ZlSheet sheet,ZlRow row,Integer index,Object content,boolean hold,int width){
        this(wb,sheet,row,index,content,hold,width,(short)11);
    }


    public ZlCell(XSSFWorkbook wb,ZlSheet sheet,ZlRow row,Integer index,Object content,boolean hold,short fontSize){
        this(wb,sheet,row,index,content,hold,0,fontSize);
    }

    public ZlCell(XSSFWorkbook wb,ZlSheet sheet,ZlRow row,Integer index,Object content,boolean hold,int width,short fontSize){
        this.wb = wb;
        this.sheet = sheet;
        this.row = row;
        this.cell = row.getRow().createCell(index);
        this.cellIndex = index;
        this.style = wb.createCellStyle();
        this.font = wb.createFont();
        //默认宋体
        this.font.setFontName("宋体");
        this.font.setBold(hold);
        this.font.setFontHeightInPoints(fontSize);

        this.style.setFont(font);
        //默认横 纵向居中
        this.style.setAlignment(HorizontalAlignment.CENTER);
        this.style.setVerticalAlignment(VerticalAlignment.CENTER);
        //默认添加边线
        this.style.setBorderTop(BorderStyle.THIN);
        this.style.setBorderBottom(BorderStyle.THIN);
        this.style.setBorderLeft(BorderStyle.THIN);
        this.style.setBorderRight(BorderStyle.THIN);
        this.cell.setCellStyle(style);
        this.cellNo = UUID.randomUUID().toString();
        setCellValue(content);
        //宽度
        if(width > 0){
            setColumnWidth(width);
        }
    }

    public void setContent(Object content) {
        this.content = content;
        setCellValue(content);
    }

    private void setCellValue(Object content){
        this.content = content;
        if(content instanceof  Number){
            this.cell.setCellValue(((Number) content).doubleValue());
        }else if(content instanceof String){
            this.cell.setCellValue((String) content);
        }else if(content instanceof Date){
            this.cell.setCellValue((Date) content);
        }else if(content instanceof Boolean){
            this.cell.setCellValue((Boolean) content);
        }else{
            this.cell.setCellValue(String.valueOf(content));
        }
    }


    /**
     * 设置字体类型 如宋体
     */
    public ZlCell setFontName(String font){
        this.font.setFontName(font);
        return this;
    }

    /**
     * 设置是否为粗体
     */
    public ZlCell setFontBold(boolean bold){
        this.font.setBold(bold);
        return this;
    }

    /**
     * 设置字体高度
     */
    public ZlCell setFontHeight(short height){
        this.font.setFontHeightInPoints(height);
        return this;
    }

    /**
     * 设置横向内容方向
     */
    public ZlCell setHorizontalAlignment(HorizontalAlignment alignment){
        this.style.setAlignment(alignment);
        this.cell.setCellStyle(style);
        return this;
    }

    /**
     * 设置纵向内容方向
     */
    public ZlCell setVerticalAlignment(HorizontalAlignment alignment){
        this.style.setAlignment(alignment);
        this.cell.setCellStyle(style);
        return this;
    }

    /**
     * 上边线
     */
    public ZlCell setBorderTop(BorderStyle borderStyle){
        this.style.setBorderTop(borderStyle);
        this.cell.setCellStyle(style);
        return this;
    }

    /**
     * 下边线
     */
    public ZlCell setBorderBottom(BorderStyle borderStyle){
        this.style.setBorderBottom(borderStyle);
        this.cell.setCellStyle(style);
        return this;
    }

    /**
     * 左边线
     */
    public ZlCell setBorderLeft(BorderStyle borderStyle){
        this.style.setBorderLeft(borderStyle);
        this.cell.setCellStyle(style);
        return this;
    }

    /**
     * 右边线
     */
    public ZlCell setBorderRight(BorderStyle borderStyle){
        this.style.setBorderRight(borderStyle);
        this.cell.setCellStyle(style);
        return this;
    }

    public ZlCell mergeCell(String endRowNo,String endCellNo){
        int firstRow = this.row.getRowIndex();
        int firstCell = this.getCellIndex();
        List<ZlRow> rows =  this.sheet.getRows();
        ZlRow endRow = rows.stream().filter(r->r.getRowNo().equalsIgnoreCase(endRowNo)).findFirst().orElse(null);
        if(endRow == null){
            return this;
        }
        ZlCell endCell = endRow.getCells().stream().filter(c->c.getCellNo().equalsIgnoreCase(endCellNo)).findFirst().orElse(null);
        if(endCell == null){
            return this;
        }
        if(firstRow == endRow.getRowIndex()){
            //如果是同行的列合并
            for(int i = firstCell ; i<= endCell.getCellIndex() ; i++){
                for(ZlCell cell : endRow.getCells()){
                    if(cell.getCellIndex() == i){
                        cell.setCellNo(this.cellNo);
                    }
                }
            }
        }
        endCell.setCellNo(this.cellNo);
        CellRangeAddress region = new CellRangeAddress(firstRow, endRow.getRowIndex(), firstCell, endCell.getCellIndex());
        this.sheet.getSheet().addMergedRegion(region);
        return this;
    }


    public ZlCell setColumnWidth(int width){
        this.sheet.getSheet().setColumnWidth(this.cell.getColumnIndex(),width * 256);
        return this;
    }


    public String getCellNo() {
        return cellNo;
    }

    public void setCellNo(String cellNo) {
        this.cellNo = cellNo;
    }

    public int getCellIndex() {
        return cellIndex;
    }

    public void setCellIndex(int cellIndex) {
        this.cellIndex = cellIndex;
    }

    public Object getContent() {
        return content;
    }

    public ZlCell setBackground(IndexedColors background) {
        this.background = background;
        for(ZlCell cell :this.row.getCells()){
            if(cell.getCellNo().equalsIgnoreCase(this.cellNo)){
                cell.getCell().getCellStyle().setFillBackgroundColor(background.getIndex());
            }
        }

        return this;
    }

    public ZlCell setFontColor(IndexedColors fontColor) {
        this.fontColor = fontColor;
        this.font.setColor(fontColor.getIndex());
        this.cell.getCellStyle().setFont(this.font);
        return this;
    }

    public Cell getCell() {
        return cell;
    }


    public ZlCell addImage(String filePath){
        try {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            File file = new File(filePath);
            BufferedImage bufferImg = ImageIO.read(file);
            String imageType = filePath.substring(filePath.length()-3);
            ImageIO.write(bufferImg, imageType, outputStream);
            XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0,
                    this.cellIndex, this.row.getRowIndex(),  this.cellIndex+1, this.row.getRowIndex() +1);
            anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE);
            //插入图片
            this.sheet.getPatriarch().createPicture(anchor, wb.addPicture(outputStream.toByteArray(), XSSFWorkbook.PICTURE_TYPE_JPEG));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return this;
    }


    private ByteArrayOutputStream inputToOutput(InputStream inputStream){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            byte[] buffer = new byte[1024];

            int len;
            while ((len = inputStream.read(buffer)) > -1) {
                baos.write(buffer, 0, len);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return baos;
    }
}

ZlRow类

import lombok.Data;
import org.apache.poi.ss.usermodel.Row;
import java.util.LinkedList;
import java.util.UUID;
/**
 * row
 */
@Data
public class ZlRow {

    /**
     * 行ID
     */
    private String rowNo;
    //行
    private Row row;
    //sheet对象
    private ZlSheet sheet;

    private int rowIndex;
    /**
     * 所有列
     */
    private LinkedList<ZlCell> cells = new LinkedList<>();

    public ZlRow(ZlSheet sheet,Integer index){
        this(sheet,index,23);
    }

    public ZlRow(ZlSheet sheet,Integer index,Integer height){
        this.rowIndex = index;
        this.sheet = sheet;
        this.row = this.sheet.getSheet().createRow(index);
        //默认设置行数为23
        this.row.setHeightInPoints(height);
        this.rowNo = UUID.randomUUID().toString();
    }

    private ZlRow addCell(ZlCell cell){
        this.cells.addLast(cell);
        return this;
    }

}

ZlSheet类

import lombok.Data;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * sheet
 */
@Data
public class ZlSheet {

    /**
     * 工作簿
     */
    private XSSFWorkbook wb;

    private XSSFSheet sheet;

    /**
     * 当前行
     */
    private int rowIndex = 0;

    /**
     * 当前列
     */
    private int cellIndex = 0;

    /**
     * 当前操作的row
     */
    private ZlRow nowRow;

    /**
     * 当前操作的cell
     */
    private ZlCell nowCell;


    private XSSFDrawing patriarch;

    /**
     * 所有行
     */
    private LinkedList<ZlRow> rows = new LinkedList<>();


    public ZlSheet(XSSFWorkbook wb){
        this(wb,"sheet");
    }

    public ZlSheet(XSSFWorkbook wb,String sheetName){
        this.wb = wb;
        this.sheet = this.wb.createSheet(sheetName);
        //隐藏网格线  是否显示网格线  true显示,false 不显示
        this.sheet.setDisplayGridlines(false);
        this.patriarch = sheet.createDrawingPatriarch();
    }


    public List<ZlRow> getRows() {
        return rows;
    }

    /**
     * 创建下一个cell
     */
    public ZlSheet createNextCell(Object content){
        if(this.nowRow == null){
            nextRow();
        }
        ZlCell cell = new ZlCell(this.wb,this,this.nowRow,this.cellIndex,content);
        this.cellIndex = this.cellIndex + 1;
        this.nowCell = cell;
        this.nowRow.getCells().addLast(cell);
        return this;
    }

    /**
     * 创建下一个cell
     */
    public ZlSheet createNextCell(Object content,boolean hold){
        ZlCell cell = new ZlCell(this.wb,this,this.nowRow,this.cellIndex,content,hold);
        this.cellIndex = this.cellIndex + 1;
        this.nowCell = cell;
        this.nowRow.getCells().addLast(cell);
        return this;
    }

    /**
     * 创建下一个cell
     */
    public ZlSheet createNextCell(Object content,boolean hold,int width){
        ZlCell cell = new ZlCell(this.wb,this,this.nowRow,this.cellIndex,content,hold,width);
        this.cellIndex = this.cellIndex + 1;
        this.nowCell = cell;
        this.nowRow.getCells().addLast(cell);
        return this;
    }

    /**
     * 创建下一个cell
     */
    public ZlSheet createNextCell(Object content,boolean hold,int width,short fontSize){
        ZlCell cell = new ZlCell(this.wb,this,this.nowRow,this.cellIndex,content,hold,width,fontSize);
        this.cellIndex = this.cellIndex + 1;
        this.nowCell = cell;
        this.nowRow.getCells().addLast(cell);
        return this;
    }


    /**
     * 创建下一个图片cell
     */
    public ZlSheet createNextImageCell(int width,String url){
        ZlCell cell = new ZlCell(this.wb,this,this.nowRow,this.cellIndex,width,url);
        this.cellIndex = this.cellIndex + 1;
        this.nowCell = cell;
        this.nowRow.getCells().addLast(cell);
        return this;
    }

    /**
     * 创建下一个cell 用多个单元格合并
     * @param indexNumber  下一个单元格的结束索引
     * @param content   单元格内容
     * @param hold   是否粗体
     */
    public ZlSheet createCell(Integer indexNumber,Object content,boolean hold){
        this.createCell(indexNumber,content,hold,0,(short)13);
        return this;
    }

    /**
     * 创建下一个cell 用多个单元格合并
     * @param indexNumber  下一个单元格的结束索引
     * @param content   单元格内容
     * @param hold   是否粗体
     * @param fontSize  字体大小
     */
    public ZlSheet createCell(Integer indexNumber,Object content,boolean hold,short fontSize){
        this.createCell(indexNumber,content,hold,0,fontSize);
        return this;
    }

    /**
     * 创建下一个cell 用多个单元格合并
     * @param indexNumber  下一个单元格的结束索引
     * @param content   单元格内容
     * @param width   单元格宽度
     * @param hold   是否粗体
     */
    public ZlSheet createCell(Integer indexNumber,Object content,int width,boolean hold){
        this.createCell(indexNumber,content,hold,width,(short)13);
        return this;
    }

    /**
     * 创建一个大的cell单元格,合并多个横向单元格
     */
    public ZlSheet createCell(Integer indexNumber,Object content,boolean hold,int width,short fontSize){
        ZlCell firstCell = null;
        ZlCell lastCell = null;
        for(int i = this.cellIndex ; i < indexNumber ; i ++){
            ZlCell cell = new ZlCell(this.wb,this,this.nowRow,i,content,hold,width,fontSize);
            if(firstCell == null) {
                firstCell = cell;
            }
            if(i == indexNumber - 1){
                lastCell = cell;
            }
            this.nowRow.getCells().addLast(cell);
        }
        if(firstCell != null && lastCell != null) {
            firstCell.mergeCell(this.nowRow.getRowNo(), lastCell.getCellNo());
        }
        this.nowCell = firstCell;
        this.cellIndex = indexNumber;
        return this;
    }

    /**
     * 供 newCell使用的方法
     */
    public ZlSheet createNextCell(ZlCell cell){
        this.cellIndex = this.cellIndex + 1;
        this.nowCell = cell;
        this.nowRow.getCells().addLast(cell);
        return this;
    }

    /**
     * 创建一个可以自定义cell各种风格
     */
    public ZlCell newCell(Object content,boolean hold,int width,short fontSize){
        return new ZlCell(this.wb,this,this.nowRow,this.cellIndex,content,hold,width,fontSize);
    }

    public ZlCell newMergeCell(Integer indexNumber,Object content,boolean hold){
        return newMergeCell(indexNumber,content,hold,0,(short)13);
    }

    public ZlCell newMergeCell(Integer indexNumber,Object content,boolean hold,short fontSize){
        return newMergeCell(indexNumber,content,hold,0,fontSize);
    }

    /**
     * 创建一个大的cell单元格,合并多个横向单元格 newCell使用的方法
     */
    public ZlCell newMergeCell(Integer indexNumber,Object content,boolean hold,int width,short fontSize){
        ZlCell firstCell = null;
        ZlCell lastCell = null;
        for(int i = this.cellIndex ; i < indexNumber ; i ++){
            ZlCell cell = new ZlCell(this.wb,this,this.nowRow,i,content,hold,width,fontSize);
            if(firstCell == null) {
                firstCell = cell;
            }
            if(i == indexNumber - 1){
                lastCell = cell;
            }
            this.nowRow.getCells().addLast(cell);
        }
        if(firstCell != null && lastCell != null) {
            firstCell.mergeCell(this.nowRow.getRowNo(), lastCell.getCellNo());
        }
        this.nowCell = firstCell;
        this.cellIndex = indexNumber;
        return firstCell;
    }

    /**
     * 供 newCell使用的方法
     */
    public ZlSheet createCell(ZlCell cell){
        this.nowCell = cell;
        this.nowRow.getCells().addLast(cell);
        return this;
    }


    /**
     * 合并上方单元格
     */
    public ZlSheet mergeTop(Integer indexNumber){
        ZlCell topCell  = this.rows.get(this.rowIndex - 1 - indexNumber).getCells().get(this.cellIndex - 1);
        topCell.setContent(this.nowCell.getContent());
        topCell.mergeCell(this.nowRow.getRowNo(),this.nowCell.getCellNo());
        return this;
    }


    /**
     * 下一行
     */
    public ZlSheet nextRow(){
        this.nextRow(23);
        return this;
    }

    /**
     * 下一行
     */
    public ZlSheet nextRow(int height){
        ZlRow row = new ZlRow(this,this.rowIndex,height);
        this.rows.addLast(row);
        this.nowRow = row;
        this.cellIndex = 0;
        this.rowIndex = this.rowIndex + 1;
        return this;
    }


    /**
     * 写入文件
     */
    public void writeFile(String path){
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        try {
            this.wb.write(stream);
            File file = new File(path);
            if(!file.exists()){
                file.createNewFile();
            }
            FileOutputStream outputStream = new FileOutputStream(file);
            stream.writeTo(outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

下面是测试:

import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class BaseEntity {

    private String username;

    private String projectName;

    private String startTime;

    private String calMoney;

    private String remark;



public static void main(String[] args) {
        List<BaseEntity> entities = new ArrayList<>();
        BaseEntity entity = new BaseEntity()
                .setUsername("张无忌").setStartTime("2022-04-01").setProjectName("启航计划")
                .setCalMoney("10000")
                .setRemark("备注");
        BaseEntity entity1 = new BaseEntity()
                .setUsername("杨潇").setStartTime("2021-05-01").setProjectName("学业辅导")
                .setCalMoney("20000")
                .setRemark("你好世界");
        entities.add(entity);
        entities.add(entity1);
        //1.创建Excel对象
        XSSFWorkbook wb = new XSSFWorkbook();
        ZlSheet sheet = new ZlSheet(wb);
        sheet.nextRow(34)
                .createCell(sheet.newMergeCell(11,"应收结算单",true,(short)18).setBackground(IndexedColors.BLUE))
                .nextRow(34)
                .createNextCell(sheet.newCell("结算公司名称:",true,30,(short)14).setBackground(IndexedColors.BLUE))
                .createCell(4,"上海至和教育",false,(short)15)
                .nextRow()
                .nextRow(25)
                .createNextCell("渠道负责人:",true,25,(short)13)
                .createCell(3,"张三",false,15,(short)13)
                .createNextCell("结算总金额",true,25,(short)13)
                .createCell(6, BigDecimal.valueOf(1000),false,15,(short)13)
                .createCell(9,"结算日期",true,10,(short)13)
                .createCell(11,"2022-03-01",false,15,(short)13)
                .nextRow()
                .nextRow(25)
                .createCell(11,"项目明细",true,(short)18)
                .nextRow(23)
                .createNextCell("学生姓名",true,25,(short)13)
                .createCell(3,"项目名称",true,15,(short)13)
                .createCell(5,"服务开始时间",true,18,(short)13)
                .createCell(7,"项目结算金额",true,15,(short)13)
                .createCell(11,"其他备注",true,15,(short)13);
        for(BaseEntity en : entities){
            sheet.nextRow(23)
                    .createNextCell(en.getUsername(),false,25,(short)13)
                    .createCell(3,en.getProjectName(),false,15,(short)13)
                    .createCell(5,en.getStartTime(),false,18,(short)13)
                    .createCell(7,en.getCalMoney(),false,15,(short)13)
                    .createCell(11,en.getRemark(),false,15,(short)13)
                    .createNextImageCell(30,"D:\图片\IMG_6180.JPG");
        }
        sheet.nextRow().nextRow(25)
                .createCell(11,"结算信息",true,(short)18)
                .nextRow(23)
                .createNextCell("付款方名称",true,25,(short)13)
                .createCell(5,"",true,15,(short)13)
                .createCell(7,"收款方名称",true,15,(short)13)
                .createCell(11,"上海天华科技有限公司",true,0,(short)13)
                .nextRow(23)
                .createNextCell("税号",true,25,(short)13)
                .createCell(5,"",true,15,(short)13)
                .createCell(7,"税号",true,15,(short)13)
                .createCell(11,"49872587324879285",true,0,(short)13)
                .nextRow(23)
                .createNextCell("银行账号",true,25,(short)13)
                .createCell(5,"",true,15,(short)13)
                .createCell(7,"银行账号",true,15,(short)13)
                .createCell(11,"3597824878256328",true,0,(short)13)
                .nextRow(23)
                .createNextCell("银行支行信息",true,25,(short)13)
                .createCell(5,"",true,15,(short)13)
                .createCell(7,"银行支行信息",true,15,(short)13)
                .createCell(11,"中国银行上海浦东分行营业部",true,0,(short)13)
                .nextRow(23)
                .createNextCell("签字",true,25,(short)13)
                .createCell(5,"",true,15,(short)13)
                .createCell(7,"签字",true,15,(short)13)
                .createCell(11,"",true,0,(short)13);
        sheet.writeFile("D:\文档\test\test1.xlsx");
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鱼香Ross

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值