0.使用jar:

<dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.9</version>
        </dependency>

最终结果,生成excel文件,上传到oss再下载,可以留档,但是会慢些;

1.controller

/**
     * 条件下载账单信息excel
     *
     * @param orderCode 检索条件
     * @param billDtStart 检索条件
     * @param billDtEnd 检索条件
     * @param state 检索条件
     * @param corganCodes 检索条件
     * @param status 检索条件
     * @param flag 检索条件
     * @param userNameLike 检索条件
     * @param capitalSide 资金方
     * @param response 相应信息
     */
    @GetMapping("/downloadBillExcel")
    public void downloadBillExcel(@RequestParam(required = false) String orderCode,
                                  @RequestParam(required = false) String billDtStart,
                                  @RequestParam(required = false) String billDtEnd,
                                  @RequestParam(required = false) String state,
                                  @RequestParam(required = false) String corganCodes,
                                  @RequestParam(required = false) String status,
                                  @RequestParam(required = false) String flag,
                                  @RequestParam(required = false) String userNameLike,
                                  @RequestParam(required = false) String capitalSide, HttpServletResponse response) {
        log.info(
                "条件下载账单信息excel-orderCode:{},billDtStart:{},billDtEnd:{},state:{},corganCodes:{},status:{},flag:{},userNameLike:{}",
                orderCode, billDtStart, billDtEnd, state, corganCodes, status, flag, userNameLike);
        PageMapperDto mapperDto = new PageMapperDto();
        mapperDto.setPageStart(null);
        mapperDto.setPageSize(null);
        mapperDto.setUserName(userNameLike);
        mapperDto.setOrderCode(orderCode);
        mapperDto.setCorganCodeListByParam(VehicleUtils.stringToList(corganCodes));
        mapperDto.setFlagList(VehicleUtils.stringToList(flag));
        mapperDto.setStateList(VehicleUtils.stringToList(state));
        mapperDto.setStatusList(VehicleUtils.stringToList(status));
        mapperDto.setCapitalSideList(VehicleUtils.stringToList(capitalSide));
        if (StringUtils.isNotBlank(billDtStart) && StringUtils.isNotBlank(billDtEnd)) {
            mapperDto
                    .setBillDtStart(DateUtils.date2String(DateUtils.parseTimestampToDate(Long.parseLong(billDtStart))));
            mapperDto.setBillDtEnd(DateUtils.date2String(DateUtils.parseTimestampToDate(Long.parseLong(billDtEnd))));
        }
        log.info("条件下载账单信息mapperDto:{}", JSON.toJSON(mapperDto));

        try {
            File file = orderBillingService.genFileByCondition(mapperDto);

            commonService.downloadFile(file, response);
        } catch (Exception e) {
            log.error("Exception:{}", JSON.toJSON(e));
        }
    }

2.serviceImpl

/**
     * 条件获取账单excel文件
     *
     * @param mapperDto 检索条件
     * @return excel文件地址
     */
    @Override
    public File genFileByCondition(PageMapperDto mapperDto) {
        List<OrderBillingPageResponseDto> responseDtoList = crudMapper.findOrderBillingPage(mapperDto);
        log.info("条件查询账单:{}", responseDtoList.size());

        // 文件存放目录
        String dir = tempFilePath;
        BankUtils.mkDir(dir);

        String billExcelPath = String.format(OrderBillingConstants.STRING_FORMAT_4, dir,
                OrderBillingConstants.DOWNLOAD_EXCEL_NAME_ORDER_BILL, BankUtils.getCurrentDateTime(),
                OrderBillingConstants.EXCEL_FORMAT_XLS);
        log.info("准备生成的账单excel文件地址:{}", billExcelPath);

        // sheet页名称
        String sheetName = OrderBillingConstants.DOWNLOAD_EXCEL_NAME_ORDER_BILL;

        // 默认列宽
        String defaultColumnLength = OrderBillingConstants.STRING_THIRTY;

        // 列宽
        List<String> columnLengthList = new ArrayList<>();

        // 标题名称
        Object[] nameObj = Stream
                .of("订单编号", "账单期数", "账单日期", "承租人姓名", "申请时间", "分公司", "分期金额", "应还本金", "应还利息", "逾期违约金", "逾期违约罚息", "代扣结果",
                        "资方业务状态", "账单标记", "账单状态", "是否已逾期", "实际还款日期", "已还月租", "已还本金", "已还利息", "已还违约金", "已还罚息", "待还月租",
                        "待还本金", "待还利息", "待还违约金", "待还罚息")
                .toArray();
        String[] title = new String[nameObj.length];
        for (int i = 0; i < title.length; i++) {
            title[i] = nameObj[i].toString();
            if (i == 0 || i == 4 || i == 5) {
                columnLengthList.add("30");
            } else {
                columnLengthList.add("20");
            }
        }

        int size = responseDtoList.size();
        String[][] context = new String[size][title.length];
        if (CollectionUtils.isNotEmpty(responseDtoList)) {
            responseDtoList.sort(Comparator.comparing(OrderBillingPageResponseDto::getId));
            for (int i = 0; i < size; i++) {
                OrderBillingPageResponseDto responseDto = responseDtoList.get(i);
                log.info("********responseDto:{}", JSON.toJSON(responseDto));
                for (int j = 0; j < title.length; j++) {
                    if (j == 0) {
                        // 订单编号
                        context[i][j] = responseDto.getOrderCode();
                    } else if (j == 1) {
                        // 账单期数
                        context[i][j] = responseDto.getBillPeriod().toString();
                    } else if (j == 2) {
                        // 账单日期
                        context[i][j] = DateUtils.formatDate(responseDto.getBillDt(),
                                OrderBillingConstants.TIME_FORMAT);
                    } else if (j == 3) {
                        // 承租人姓名
                        context[i][j] = String.valueOf(responseDto.getUserName());
                    } else if (j == 4) {
                        // 申请时间
                        if (null != responseDto.getApplyTime()) {
                            context[i][j] = DateUtils.formatDate(responseDto.getApplyTime(),
                                    VehicleConstants.TIANYI_TIME_FORMAT);
                        }
                    } else if (j == 5) {
                        // 分公司
                        context[i][j] = responseDto.getOrganName();
                    } else if (j == 6) {
                        // 分期金额
                        context[i][j] = String.valueOf(responseDto.getBillFee());
                    } else if (j == 7) {
                        // 账单应还本金
                        context[i][j] = String.valueOf(responseDto.getBillCapital());
                    } else if (j == 8) {
                        // 账单应还利息
                        context[i][j] = String.valueOf(responseDto.getBillInterest());
                    } else if (j == 9) {
                        // 逾期违约金
                        context[i][j] = String.valueOf(responseDto.getOverdueContractAmt());
                    } else if (j == 10) {
                        // 逾期罚息
                        context[i][j] = String.valueOf(responseDto.getOverduePenalty());
                    } else if (j == 11) {
                        // 代扣结果
                        context[i][j] = responseDto.getWithholdResult();
                    } else if (j == 12) {
                        BillStateEnum billStateEnum = BillStateEnum.getEnumByCode(responseDto.getState());
                        // 资方业务状态
                        if (billStateEnum != null) {
                            context[i][j] = billStateEnum.getMsg();
                        }
                    } else if (j == 13) {
                        // 账单标记
                        BillFlagEnum billFlagEnum = BillFlagEnum.getEnumByCode(responseDto.getFlag());
                        if (billFlagEnum != null) {
                            context[i][j] = billFlagEnum.getMsg();
                        }
                    } else if (j == 14) {
                        // 状态
                        BillStatusEnum billStatusEnum = BillStatusEnum.getEnumByCode(responseDto.getStatus());
                        if (billStatusEnum != null) {
                            context[i][j] = billStatusEnum.getMsg();
                        }
                    } else if (j == 15) {
                        // 是否已逾期
                        YesNoEnum yesNoEnum = YesNoEnum.getEnumByCode(responseDto.getIsOverdue());
                        if (yesNoEnum != null) {
                            context[i][j] = yesNoEnum.getMsg();
                        }
                    } else if (j == 16) {
                        // 实际还款日期
                        if (null != responseDto.getBillRepayDt()) {
                            context[i][j] = DateUtils.formatDate(responseDto.getBillRepayDt(),
                                    OrderBillingConstants.TIME_FORMAT);
                        }
                    } else if (j == 17) {
                        // 已还月租
                        context[i][j] = responseDto.getPaidFee().toString();
                    } else if (j == 18) {
                        // 已还本金
                        context[i][j] = responseDto.getBillCapital()
                                .subtract(responseDto.getUnpaidCapital())
                                .toString();
                    } else if (j == 19) {
                        // 已还利息
                        context[i][j] = responseDto.getBillInterest()
                                .subtract(responseDto.getUnpaidIntrest())
                                .toString();
                    } else if (j == 20) {
                        // 已还违约金
                        context[i][j] = responseDto.getOverdueContractAmt()
                                .subtract(responseDto.getUnpaidContractAmt())
                                .toString();
                    } else if (j == 21) {
                        // 已还罚息
                        context[i][j] = responseDto.getOverduePenalty()
                                .subtract(responseDto.getUnpaidPenalty())
                                .toString();
                    } else if (j == 22) {
                        // 待还月租
                        context[i][j] = responseDto.getUnpaidFee().toString();
                    } else if (j == 23) {
                        // 待还本金
                        context[i][j] = responseDto.getUnpaidCapital().toString();
                    } else if (j == 24) {
                        // 待还利息
                        context[i][j] = responseDto.getUnpaidIntrest().toString();
                    } else if (j == 25) {
                        // 待还违约金
                        context[i][j] = responseDto.getUnpaidContractAmt().toString();
                    } else {
                        // 待还罚息
                        context[i][j] = responseDto.getUnpaidPenalty().toString();
                    }
                }
            }
        }

        List<Label> labelList = new ArrayList<>();
        List<ExcelMergeCellsDto> mergeCellsDtoList = new ArrayList<>();

        // 生成excel文件
        ExcelUtils.exportExcel(billExcelPath, sheetName, defaultColumnLength, columnLengthList, title, context,
                labelList, mergeCellsDtoList);
        return new File(billExcelPath);
    }

3. ExcelUtils

import java.io.File;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import com.yifenqi.dto.ExcelMergeCellsDto;

import jxl.CellView;
import jxl.Workbook;
import jxl.format.Alignment;
import jxl.format.Border;
import jxl.format.BorderLineStyle;
import jxl.format.Colour;
import jxl.format.UnderlineStyle;
import jxl.format.VerticalAlignment;
import jxl.write.Label;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
import lombok.extern.slf4j.Slf4j;

/**
 * Excel处理工具类
 */
@Slf4j
public class ExcelUtils {

    /**
     * WORD_TO_PDF_OPERAND
     */
    private static final Integer WORD_TO_PDF_OPERAND  = 17;

    /**
     * PPT_TO_PDF_OPERAND
     */
    private static final Integer PPT_TO_PDF_OPERAND   = 32;

    /**
     * EXCEL_TO_PDF_OPERAND
     */
    private static final Integer EXCEL_TO_PDF_OPERAND = 0;

    /**
     * 生成Excel到filePath路径中
     *
     * @param filePath 生成Excel的保存路径
     * @param sheetName Excel中sheet名
     * @param defaultColumnLength 整个Excel的默认单元格宽度
     * @param columnLengthList 定义每个列宽
     * @param title 标题
     * @param context 内容
     * @param labelList 需要合并处文字设置
     * @param mergeCellsDtoList 单元格合并坐标列表
     */
    public static void exportExcel(String filePath, String sheetName, String defaultColumnLength,
                                   List<String> columnLengthList, String[] title, String[][] context,
                                   List<Label> labelList, List<ExcelMergeCellsDto> mergeCellsDtoList) {
        exportExcel(filePath, sheetName, defaultColumnLength, columnLengthList, title, context, labelList,
                mergeCellsDtoList, null);
    }

    /**
     * 生成Excel到filePath路径中
     *
     * @param filePath 生成Excel的保存路径
     * @param sheetName Excel中sheet名
     * @param defaultColumnLength 整个Excel的默认单元格宽度
     * @param columnLengthList 定义每个列宽
     * @param title 标题
     * @param context 内容
     * @param labelList 需要合并处文字设置
     * @param mergeCellsDtoList 单元格合并坐标列表
     * @param rowHeightMap 定义行高
     */
    public static void exportExcel(String filePath, String sheetName, String defaultColumnLength,
                                   List<String> columnLengthList, String[] title, String[][] context,
                                   List<Label> labelList, List<ExcelMergeCellsDto> mergeCellsDtoList,
                                   Map<Integer, Integer> rowHeightMap) {

        //操作执行
        try {
            //t.xls为要新建的文件名
            WritableWorkbook book = Workbook.createWorkbook(new File(filePath));
            //生成名为“第一页”的工作表,参数0表示这是第一页
            WritableSheet sheet = book.createSheet(sheetName, 0);

            if (!StringUtils.isBlank(defaultColumnLength)) {
                sheet.getSettings().setDefaultColumnWidth(Integer.valueOf(defaultColumnLength));
            }

            //Excel相关默认设置
            WritableCellFormat titleFormat = genTitleFormat();
            WritableCellFormat contentFormat = genContentFormat();
            CellView cellView = new CellView();
            cellView.setAutosize(true);

            //写入标题
            for (int i = 0; i < title.length; i++) {
                //title
                sheet.addCell(new Label(i, 0, title[i], titleFormat));
                if (!CollectionUtils.isEmpty(columnLengthList) && columnLengthList.size() >= title.length) {
                    sheet.setColumnView(i, Integer.valueOf(columnLengthList.get(i)));
                }
            }

            //Excel的内容
            //context
            for (int i = 0; i < context.length; i++) {
                for (int j = 0; j < context[i].length; j++) {
                    sheet.addCell(new Label(j, i + 1, context[i][j], contentFormat));
                }
            }

            //合并单元格
            if (!CollectionUtils.isEmpty(labelList)) {
                for (int i = 0; i < labelList.size(); i++) {
                    addCell(sheet, labelList.get(i));
                }
            }

            if (!CollectionUtils.isEmpty(mergeCellsDtoList)) {
                for (int i = 0; i < mergeCellsDtoList.size(); i++) {
                    ExcelMergeCellsDto record = mergeCellsDtoList.get(i);
                    mergeCells(sheet, record.getM(), record.getN(), record.getP(), record.getQ());
                }
            }

            //设置行高
            if (null != rowHeightMap) {
                for (Map.Entry<Integer, Integer> entry : rowHeightMap.entrySet()) {
                    sheet.setRowView(entry.getKey(), entry.getValue());
                }
            }
            // 空白行,列删除
            removeColumns(sheet);

            //写入数据
            book.write();
            //关闭文件
            book.close();
        } catch (Exception e) {
            log.error("exportExcel error{}", e);
        }
    }

    /**
     * 组装数据
     *
     * @param book 文件
     * @param sheetName Excel中sheet名
     * @param defaultColumnLength 整个Excel的默认单元格宽度
     * @param columnLengthList 定义每个列宽
     * @param title 标题
     * @param context 内容
     * @param labelList 需要合并处文字设置
     * @param mergeCellsDtoList 单元格合并坐标列表
     */
    public static void exportExcelData(WritableWorkbook book, String sheetName, String defaultColumnLength,
                                       List<String> columnLengthList, String[] title, String[][] context,
                                       List<Label> labelList, List<ExcelMergeCellsDto> mergeCellsDtoList) {
        exportExcelData(book, sheetName, defaultColumnLength, columnLengthList, title, context, labelList,
                mergeCellsDtoList, null);
    }

    /**
     * 组装Excel数据
     *
     * @param book ExcelBook
     * @param sheetName Excel中sheet名
     * @param defaultColumnLength 整个Excel的默认单元格宽度
     * @param columnLengthList 定义每个列宽
     * @param title 标题
     * @param context 内容
     * @param labelList 需要合并处文字设置
     * @param mergeCellsDtoList 单元格合并坐标列表
     * @param rowHeightMap 定义行高
     */
    public static void exportExcelData(WritableWorkbook book, String sheetName, String defaultColumnLength,
                                       List<String> columnLengthList, String[] title, String[][] context,
                                       List<Label> labelList, List<ExcelMergeCellsDto> mergeCellsDtoList,
                                       Map<Integer, Integer> rowHeightMap) {
        //操作执行
        try {
            //生成名为“第一页”的工作表,参数0表示这是第一页
            WritableSheet sheet = book.createSheet(sheetName, 0);

            if (!StringUtils.isBlank(defaultColumnLength)) {
                sheet.getSettings().setDefaultColumnWidth(Integer.valueOf(defaultColumnLength));
            }

            //Excel相关默认设置
            WritableCellFormat titleFormat = genTitleFormat();
            WritableCellFormat contentFormat = genContentFormat();
            CellView cellView = new CellView();
            cellView.setAutosize(true);

            //写入标题
            for (int i = 0; i < title.length; i++) {
                //title
                sheet.addCell(new Label(i, 0, title[i], titleFormat));
                if (!CollectionUtils.isEmpty(columnLengthList) && columnLengthList.size() >= title.length) {
                    sheet.setColumnView(i, Integer.valueOf(columnLengthList.get(i)));
                }
            }

            //Excel的内容
            //context
            for (int i = 0; i < context.length; i++) {
                for (int j = 0; j < context[i].length; j++) {
                    sheet.addCell(new Label(j, i + 1, context[i][j], contentFormat));
                }
            }

            //合并单元格
            if (!CollectionUtils.isEmpty(labelList)) {
                for (int i = 0; i < labelList.size(); i++) {
                    addCell(sheet, labelList.get(i));
                }
            }

            if (!CollectionUtils.isEmpty(mergeCellsDtoList)) {
                for (int i = 0; i < mergeCellsDtoList.size(); i++) {
                    ExcelMergeCellsDto record = mergeCellsDtoList.get(i);
                    mergeCells(sheet, record.getM(), record.getN(), record.getP(), record.getQ());
                }
            }

            //设置行高
            if (null != rowHeightMap) {
                for (Map.Entry<Integer, Integer> entry : rowHeightMap.entrySet()) {
                    sheet.setRowView(entry.getKey(), entry.getValue());
                }
            }

            // 空白行,列删除
            removeColumns(sheet);
        } catch (Exception e) {
            log.error("exportExcel error{}", e);
        }
    }

    /**
     * 特殊单元格数据设置
     *
     * @param sheet Excel中的sheet
     * @param label 页面
     */
    public static void addCell(WritableSheet sheet, Label label) {
        try {
            sheet.addCell(label);
        } catch (WriteException e) {
            log.error("addCell error{}", e);
        }
    }

    /**
     * 合并单元格.合并既可以是横向的,也可以是纵向的 WritableSheet.mergeCells(int m,int n,int p,int
     * q); 表示由(m,n)到(p,q)的单元格组成的矩形区域合并
     *
     * @param sheet Excel中的sheet
     * @param m 开始单元格x坐标
     * @param n 开始单元格y坐标
     * @param p 结束单元格x坐标
     * @param q 结束单元格y坐标
     */
    public static void mergeCells(WritableSheet sheet, int m, int n, int p, int q) {
        try {
            sheet.mergeCells(m, n, p, q);
        } catch (WriteException e) {
            log.error("mergeCells error{}", e);
        }
    }

    /**
     * Excel中的标题格式
     *
     * @return 标题格式
     */
    public static WritableCellFormat genTitleFormat() {
        WritableCellFormat cellFormat = null;
        //设置垂直居中;
        try {
            //设置字体;
            WritableFont fontTitle = new WritableFont(WritableFont.ARIAL, 14, WritableFont.BOLD, false,
                    UnderlineStyle.NO_UNDERLINE, Colour.BLACK);

            cellFormat = new WritableCellFormat(fontTitle);
            //设置背景颜色;
            cellFormat.setBackground(Colour.GRAY_25);
            //设置边框;
            cellFormat.setBorder(Border.ALL, BorderLineStyle.THIN);
            //设置自动换行;
            cellFormat.setWrap(true);
            //设置文字对齐方式;
            cellFormat.setAlignment(Alignment.CENTRE);
            cellFormat.setVerticalAlignment(VerticalAlignment.CENTRE);
        } catch (WriteException e) {
            log.error("genTitleFormat Exception is{}", e);
        }

        return cellFormat;
    }

    /**
     * Excel中的内容格式
     *
     * @return 内容格式
     */
    public static WritableCellFormat genContentFormat() {
        WritableCellFormat cellFormat = null;
        //设置垂直居中;
        try {
            //设置字体;
            WritableFont fontTitle = new WritableFont(WritableFont.ARIAL, 12, WritableFont.NO_BOLD, false,
                    UnderlineStyle.NO_UNDERLINE, Colour.BLACK);

            cellFormat = new WritableCellFormat(fontTitle);
            //设置背景颜色;
            cellFormat.setBackground(Colour.WHITE);
            //设置边框;
            cellFormat.setBorder(Border.ALL, BorderLineStyle.THIN);
            //设置自动换行;
            cellFormat.setWrap(true);
            //设置文字对齐方式;
            cellFormat.setAlignment(Alignment.LEFT);
            cellFormat.setVerticalAlignment(VerticalAlignment.CENTRE);
        } catch (WriteException e) {
            log.error("genContentFormat Exception is{}", e);
        }

        return cellFormat;
    }

    /**
     * 删除空白行
     *
     * @param sheet Excel中的sheet
     */
    public static void removeColumns(WritableSheet sheet) {
        //列数
        int rsCols = sheet.getColumns();
        //行数
        int rsRows = sheet.getRows();
        int nullCellNum;
        //统计行中为空的单元格数
        for (int i = 1; i < rsRows; i++) {
            nullCellNum = 0;
            for (int j = 0; j < rsCols; j++) {
                String val = sheet.getCell(j, i).getContents();
                val = StringUtils.trimToEmpty(val);
                if (StringUtils.isBlank(val)) {
                    nullCellNum++;
                }
                //如果nullCellNum大于或等于总的列数
                if (nullCellNum >= rsCols) {
                    sheet.removeColumn(j);
                }
            }
        }
    }
}

4.commonServiceImpl

/**
     * 下载单个文件
     *
     * @param file 文件信息
     * @param response 响应信息
     */
    @Override
    public void downloadFile(File file, HttpServletResponse response) {
        long start = System.currentTimeMillis();
        try {
            int fileBufferSize = CommConstant.FILE_BUFFER_SIZE_DEFAULT;
            DictCode dictCode = dictCodeService.findByGroupAndCodeNoCache(CommConstant.FILE_BUFFER_SIZE,
                    CommonFlag.Y.getCode());
            if (dictCode != null) {
                fileBufferSize = Integer.valueOf(dictCode.getName());
            }

            if (file == null) {
                response.getWriter().println("file is null , please check file!");
                return;
            } else if (!file.exists() || file.isDirectory()) {
                response.getWriter().println("file is error , please check file!");
                return;
            }
            log.error("下载-{}-开始", file.getName());

            response.reset();
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + new String((file.getName()).getBytes("gb2312"), "ISO8859-1"));
            InputStream in = new FileInputStream(file);
            OutputStream out = response.getOutputStream();

            byte[] car = new byte[fileBufferSize];
            int l;
            while ((l = in.read(car)) != -1) {
                out.write(car, 0, l);
            }

            out.flush();
            out.close();
            in.close();

            log.error("下载-完成-文件大小:{} , fileBufferSize:{} byte, 耗时:{} ms", VehicleUtils.getFileSize(file),
                    fileBufferSize, System.currentTimeMillis() - start);

            boolean deleteResult = IOUtils.deleteDir(file);
            log.error("下载-完成-删除本地文件:{}", deleteResult);
        } catch (Exception e) {
            log.error("下载-异常:{}", JSON.toJSON(e));
        }
    }

5.IOUtils

/**
     * 递归删除目录下的所有文件及子目录下所有文件
     *
     * @param dir 将要删除的文件目录
     * @return boolean Returns "true" if all deletions were successful. If a
     *         deletion fails, the method stops attempting to delete and returns
     *         "false".
     */
    public static boolean deleteDir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            //递归删除目录中的子目录下
            for (int i = 0; i < children.length; i++) {
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }
        // 目录此时为空,可以删除
        return dir.delete();
    }