Java导出大量数据到Excel解决方案

ResultHandler流式查询导出Excel数据

// 导出Excel头部名称
List<String> headerArray = Arrays.asList("编码","姓名");
// 数据库对应字段
List<String> fieldArray = Arrays.asList("id", "name");
// 定义Excel文件名,不带“xlsx”后缀。
String fileName = getToday()+"导出数据-共计("+count+")"+"条";
//每次导出New 一个 handler 对象。将headerArray,fieldArray,fileName 传入参数。
ExcelResultHandler<查询对象> handler = new ExcelResultHandler<查询对象>(headerArray,fieldArray,fileName) {
            public void tryFetchDataAndWriteToExcel() {
                // this 指的是 New 对象,可添加查询 条件参数。
        		// mapper的方法需要是void返回,并且参数中含ResultHandler(流式查询遍历的条件),可以加条件参数,
                // fetchSize参数必填。
                //见下图
                //<select id="export" parameterType="" resultType="" fetchSize="-2147483648">
                ***Mapper.export(this);
            }
        };
//开始调用方法。原理:调用开始后,表头写入Excel,遍历结果集,一条一条数据写入Excel,最后关闭资源。
handler.startExportExcel();

源码会判断方法返回值是否void,是否含有ResultHandler
在这里插入图片描述

ExcelResultHandler工具类:

按需修改

public abstract class ExcelResultHandler<T> implements ResultHandler<T>{
   
   private final Logger logger = LoggerFactory.getLogger(this.getClass());
   private AtomicInteger currentRowNumber = new AtomicInteger(0);
   private Sheet sheet = null;
   private List<String> headerArray ;
   private List<String> fieldArray ;
   private int totalCellNumber;
   private boolean isExportZip = true;
   private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
   private String exportFileName = UUID.randomUUID().toString().replace("-", "");
   public ExcelResultHandler(List<String> headerArray,List<String> fieldArray){
      this.headerArray = headerArray;
      this.fieldArray = fieldArray;
      this.totalCellNumber = headerArray.size();
   }
   public ExcelResultHandler(List<String> headerArray,List<String> fieldArray,boolean isExportZip){
      this(headerArray,fieldArray);
      this.isExportZip = isExportZip;

   }
   public ExcelResultHandler(List<String> headerArray,List<String> fieldArray,String exportFileName){
      this(headerArray,fieldArray);
      this.exportFileName = exportFileName;
   }
   public ExcelResultHandler(List<String> headerArray,List<String> fieldArray,String exportFileName,boolean isExportZip){
      this(headerArray,fieldArray,exportFileName);
      this.isExportZip = isExportZip;
   }
   public abstract void tryFetchDataAndWriteToExcel();
   public void handleResult(ResultContext<? extends T> resultContext) {
      Object aRowData = resultContext.getResultObject();
      callBackWriteRowdataToExcel(aRowData);
      
   }
   public void startExportExcel() {
      HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
      ZipOutputStream zos = null;
      OutputStream os = null;
      try {
         logger.info("--------->>>>写入Excel开始.." );
         //写入文件
         response.setContentType("application/octet-stream");
         response.setHeader("Content-Disposition", "attachment;filename=" + new String((exportFileName+".zip").replaceAll(" ", "").getBytes("utf-8"),"iso8859-1"));
         os = new BufferedOutputStream(response.getOutputStream());
         if(isExportZip){
            zos = new ZipOutputStream(os);
            ZipEntry zipEntry = new ZipEntry(new String((exportFileName+".xlsx").replaceAll(" ", "")));
            zos.putNextEntry(zipEntry);
         }
         SXSSFWorkbook wb = new SXSSFWorkbook();
         wb.setCompressTempFiles(false);
            sheet = wb.createSheet("Sheet 1");
           Row row = sheet.createRow(0);
           for (int cellNumber = 0; cellNumber < totalCellNumber; cellNumber++) {
              Cell cell = row.createCell(cellNumber);
                cell.setCellValue(headerArray.get(cellNumber));
           }
           tryFetchDataAndWriteToExcel();
         logger.info("--------->>>> write to excel size now is {}", currentRowNumber.get() );
         if(isExportZip){
            wb.write(zos);
         }else{
            wb.write(os);
         }
           if (wb != null) {
               wb.dispose();// 删除临时文件,很重要,否则磁盘可能会被写满
            }
           wb.close();
         logger.info("--------->>>>全部数据写入Excel完成.." );
      } catch (Exception e) {
         logger.error("系统异常,请联系管理员",e);
      } finally {
         logger.info("--------->>>>关闭流系统.." );
         if(isExportZip){
            try {if(zos!=null) zos.close();} catch (IOException e1) {logger.error("关闭流系统异常,请联系管理员",e1);    }
         }else{
            try {if(os!=null) os.close();} catch (IOException e1) {logger.error("关闭流系统异常,请联系管理员",e1);  }
         }
            
      }
   }
   @SuppressWarnings("rawtypes")
   public void callBackWriteRowdataToExcel(Object aRowData) {
      MethodAccessor methodAccessor = Reflector.getMethodAccessor(aRowData.getClass());
      currentRowNumber.incrementAndGet();
      Row row = sheet.createRow(currentRowNumber.get());
      for (int cellNumber = 0; cellNumber < totalCellNumber; cellNumber++) {
         Object value = null;
         if(aRowData instanceof Map){
            value = ((Map)aRowData).get(fieldArray.get(cellNumber));
         }else {
            value = methodAccessor.getFieldValue(aRowData, fieldArray.get(cellNumber));
         }
         Cell cell = row.createCell(cellNumber);
            if (value!=null && value instanceof Date){
               cell.setCellValue(sdf.format(value));//
            }else {
               cell.setCellValue(value==null?"":value.toString());//写入数据
            }
      }
      if(currentRowNumber.get() % 5000 == 0 ){
         logger.info("--------->>>> write to excel size now is {}", currentRowNumber.get() );
      }
   }

}

因为无返回值,前端如何知道是否下载完成?

前端用下载监听器。

var index = layer.load(3, {
    shade: [0.1, '#fff'],
    content: '<span class="loadtip">下载中</span>',
    success: function (layer) {
        layer.find('.layui-layer-content').css({
            'padding-top': '30px',
            'width': '100px',
        });
        layer.find('.loadtip').css({
            'font-size':'18px',
            'margin-left':'5px'
        });
    }
});
const xhr = new XMLHttpRequest();
xhr.open('GET', '下载接口');
xhr.send(null);
// 设置服务端的响应类型
xhr.responseType = "blob";
// 监听下载
xhr.addEventListener('progress', event => {
    // 计算出百分比
    const percent  = ((event.loaded / event.total) * 100).toFixed(2);
    console.log(`下载进度:${percent}`);
}, false);
xhr.onreadystatechange = event => {
    if (xhr.readyState == 4) {
        if (xhr.status == 200) {
            // 获取ContentType
            const contentType = xhr.getResponseHeader('Content-Type');
            // 文件名称 如果想和后端一样,需要处理编码
            const fileName = "名称.zip";
            // 创建一个a标签用于下载
            const donwLoadLink = document.createElement('a');
            donwLoadLink.download = fileName;
            donwLoadLink.href = URL.createObjectURL(xhr.response);
            // 触发下载事件,IO到磁盘
            donwLoadLink.click();
            // 释放内存中的资源
            URL.revokeObjectURL(donwLoadLink.href);
            // 关闭加载动画
            layer.close(index);
        } else if (response.status == 404) {
            alert(`文件:${file} 不存在`);
        } else if (response.status == 500) {
            alert('系统异常');
        }
    }
}

  • 20
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中可以使用Apache POI和Freemarker两个库来实现根据模板导出数据到word的解决方案。 Apache POI是一个Java API,可以用于读写Microsoft Office格式的文档,包括Word、Excel和PowerPoint等。使用Apache POI,可以在Java程序中创建、修改和读取Word文档,将数据填充到Word文档中的模板中,生成新的Word文档。 Freemarker是一个模板引擎,可以将数据填充到模板中,生成新的文本文件,包括HTML、XML、JSON和Word等。使用Freemarker,可以将数据填充到Word文档中的模板中,生成新的Word文档。 具体实现步骤如下: 1. 创建Word文档模板,使用Word软件设计好需要填充数据的文档模板。 2. 使用Apache POI读取Word文档模板,获取到需要填充数据的位置和格式。 3. 使用Freemarker将数据填充到Word文档模板中,生成新的Word文档。 4. 将生成的新的Word文档保存到文件或输出到浏览器。 这是一个基本的实现流程,具体实现细节可以参考相关的文档和示例代码。以下是一个基本的示例代码: ``` // 1. 创建Word文档模板 FileInputStream fis = new FileInputStream("template.docx"); XWPFDocument templateDoc = new XWPFDocument(fis); fis.close(); // 2. 使用Apache POI读取Word文档模板 for (XWPFParagraph paragraph : templateDoc.getParagraphs()) { List<XWPFRun> runs = paragraph.getRuns(); for (XWPFRun run : runs) { String text = run.getText(0); if (text != null && text.contains("${")) { // 找到需要填充的位置 // 可以使用正则表达式或者字符串替换等方式进行定位 } } } // 3. 使用Freemarker将数据填充到Word文档模板中 Configuration cfg = new Configuration(Configuration.VERSION_2_3_30); cfg.setDefaultEncoding("UTF-8"); cfg.setClassForTemplateLoading(this.getClass(), "/templates"); Template template = cfg.getTemplate("template.ftl"); Map<String, Object> data = new HashMap<>(); data.put("name", "张三"); data.put("age", 20); StringWriter sw = new StringWriter(); template.process(data, sw); String content = sw.toString(); // 4. 将生成的新的Word文档保存到文件或输出到浏览器 XWPFDocument newDoc = new XWPFDocument(); XWPFParagraph newParagraph = newDoc.createParagraph(); XWPFRun newRun = newParagraph.createRun(); newRun.setText(content); FileOutputStream fos = new FileOutputStream("output.docx"); newDoc.write(fos); fos.close(); newDoc.close(); ``` 上述代码使用了一个模板文件`template.ftl`,该文件中包含了需要填充的数据的位置和格式。在代码中,使用Freemarker将数据填充到模板中,生成新的内容`content`,然后将新的内容生成为一个新的Word文档并保存到文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值