利用springboot实现简单的异步导出Excel操作,解决Http请求超时问题

编写该博客主要目的是方便下次可以直接查看,好记性不如烂笔头,同时也希望各位大佬能提出各种意见,感谢!

该博客记录了 当进行Excel文件导出的时候,如果文件导出量过大导致http请求超时的情况,所以进行了异步线程导出的想法。

Controller层

首先需要在controller层加入一个注解@EnableAsync表明开启多线程操作

实现导出API接口为例子,业务逻辑:主线程执行先生成一条空OSS路径的导出记录数据,子线程就进行异步操作,将生成的Excel上传到OSS上,并将生成的OSS路径更新到该导出数据即可。

工具类

ExcelAsync工具类
package com.sgi.finance.utils;


import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.github.pagehelper.PageInfo;
import com.sgi.admin.form.report.ProductVolumeCountForm;
import com.sgi.common.msg.ObjectRestResponse;
import com.sgi.common.util.EasyPoiUtil;
import com.sgi.entity.finance.FinancingExportExcelRecord;
import com.sgi.entity.finance.FinancingService;
import com.sgi.entity.finance.FinancingServiceRepaymentRecord;
import com.sgi.finance.feign.OssServerFegin;
import com.sgi.finance.form.FinancingServicePage;
import com.sgi.finance.form.RepaymentRecordPage;
import com.sgi.finance.mapper.FinancingExportExcelRecordMapper;
import com.sgi.finance.service.IFinancingExportExcelRecordService;
import com.sgi.finance.service.IFinancingServiceRepaymentRecordService;
import com.sgi.finance.service.IFinancingServiceService;
import com.sgi.finance.vo.FinancingServiceVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import java.io.FileOutputStream;
import java.io.OutputStream;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static cn.afterturn.easypoi.excel.entity.enmus.ExcelType.HSSF;


/**
 * @Description:
 * @Modified by:
 */
@Component
@Slf4j
public class ExcelAsync {


    @Autowired
    private OssServerFegin ossServerFegin;

    @Value("${template.pdfPath}")
    private String templatePath;

    @Autowired
    FileItemUtil fileItemUtil;

    @Autowired
    private IFinancingExportExcelRecordService iFinancingExportExcelRecordService;

    @Autowired
    FinancingExportExcelRecordMapper sysExportExcelRecordMapper;

    @Autowired
    private IFinancingServiceService iFinancingServiceService;

    @Autowired
    private IFinancingServiceRepaymentRecordService iFinancingServiceRepaymentRecordService;


    /**
     *
     * @param productVolumeCountForm
     * @param sysExportExcelRecordBId
     * @throws Exception
     */
    @Async
    public synchronized void saveExportByProductChildThread(Object obj, String sysExportExcelRecordBId, String pactFile, Integer excelType) throws Exception {
        log.info("进入子线程操作..........");
//        Thread.sleep(2000);
        List<Map<String, Object>> exportMaplist = new ArrayList<>();
        List<T> rows = new ArrayList<>();
        PageInfo<T> pageInfo = new PageInfo();

        if (obj instanceof FinancingServicePage) {
            // 金融数据导出
            FinancingServicePage financingServicePage = (FinancingServicePage) obj;
            financingServicePage.setPageNum(1);
            financingServicePage.setPageSize(iFinancingServiceService.count(new LambdaQueryWrapper<>()));
            pageInfo = iFinancingServiceService.exportByFinancingService(financingServicePage);
            rows = pageInfo.getList();
        } else if (obj instanceof RepaymentRecordPage) {
            // 还款数据导出
            RepaymentRecordPage repaymentRecordPage = (RepaymentRecordPage) obj;
            repaymentRecordPage.setPageNum(1);
            repaymentRecordPage.setPageSize(iFinancingServiceRepaymentRecordService.count(new LambdaQueryWrapper<>()));
            pageInfo = iFinancingServiceRepaymentRecordService.exportByFinancingRepayment(repaymentRecordPage);
            rows = pageInfo.getList();
        } else {
            // 说明没有数据,就将导出数据置为导出失败
            updateExportExcelRecordByFail(sysExportExcelRecordBId);
            throw new Exception("异步导出错误!");
        }

        log.info("列表条数>>>>>>>>{}", rows.size());
        if (!CollectionUtils.isEmpty(rows)) {
            if (excelType.equals(1) || excelType.equals(2) || excelType.equals(3)) {
                // 金融数据导出
                EasyPoiUtil.getExportMap(pactFile, pactFile, FinancingService.class, rows, exportMaplist);
            } else if (excelType.equals(4)) {
                // 还款数据导出
                EasyPoiUtil.getExportMap(pactFile, pactFile, FinancingServiceRepaymentRecord.class, rows, exportMaplist);
            } else {
                // 说明没有数据,就将导出数据置为导出失败
                updateExportExcelRecordByFail(sysExportExcelRecordBId);
                throw new Exception("异步导出错误!");
            }
            log.debug(JSONUtil.toJsonStr(exportMaplist));
            // 生成Excel文件
            Workbook workbook = ExcelExportUtil.exportExcel(exportMaplist, HSSF);
            String path = templatePath + '\\' + pactFile + ".xls";
            log.info("excel文件路径为>>>>>>>>" + path);
            OutputStream outputStream = getOutputStream(workbook, path);
            log.info("outputStream 最后输出结果>>>>>>" + outputStream);
            //上传操作
            if (null != outputStream) {
                try {
                    FileItem fileItem = fileItemUtil.createFileItem(path, pactFile);
                    log.info("fileItem结果为>>>>>>" + fileItem);
                    getMultipartFileSysExportExcelRecord(sysExportExcelRecordBId, fileItem);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } else {
            // 说明没有数据,就将导出数据置为导出失败
            updateExportExcelRecordByFail(sysExportExcelRecordBId);
        }
    }

    /**
     * 说明没有数据,就将导出数据置为导出失败
     *
     * @param sysExportExcelRecordBId
     */
    private void updateExportExcelRecordByFail(String sysExportExcelRecordBId) {
        // 说明没有数据,就将导出数据置为导出失败
        //更新导出文件
        FinancingExportExcelRecord detailsByBIdOne = iFinancingExportExcelRecordService.getOne(new LambdaQueryWrapper<FinancingExportExcelRecord>().eq(FinancingExportExcelRecord::getBId, sysExportExcelRecordBId).eq(FinancingExportExcelRecord::getDelFlag, 0));
        detailsByBIdOne.setPactFileStatus(2);
        detailsByBIdOne.setUpdateUserId(detailsByBIdOne.getCreateUserId());
        Boolean updateFale = sysExportExcelRecordMapper.updateRecord(detailsByBIdOne.getPactFilePath(), detailsByBIdOne.getPactFileStatus(), detailsByBIdOne.getUpdateUserId(), detailsByBIdOne.getId());
        log.info("更新导出的Excel文件是否成功>>>>{}", updateFale);
    }


    /**
     * 公共方法,获取上传路径并更新导出Excel记录表
     *
     * @param sysExportExcelRecordBId
     * @param fileItem
     */
    private void getMultipartFileSysExportExcelRecord(String sysExportExcelRecordBId, FileItem fileItem) {
        MultipartFile file = new CommonsMultipartFile(fileItem);
        log.info("multi结果为>>>>>>" + file);
        boolean updateFale = false;
        String downloadFilePath = null;
        ObjectRestResponse<Map> objectRestResponse = ossServerFegin.uploadFile(file);
        if (objectRestResponse.getStatus() == 200) {
            log.info("OSS上传成功的情况>>>>>>>" + objectRestResponse.getData());
            //更新导出文件
            FinancingExportExcelRecord detailsByBIdOne = iFinancingExportExcelRecordService.getOne(new LambdaQueryWrapper<FinancingExportExcelRecord>().eq(FinancingExportExcelRecord::getBId, sysExportExcelRecordBId).eq(FinancingExportExcelRecord::getDelFlag, 0));
            detailsByBIdOne.setPactFilePath(objectRestResponse.getData().get("filePath").toString());
            detailsByBIdOne.setPactFileStatus(1);
            detailsByBIdOne.setUpdateUserId(detailsByBIdOne.getCreateUserId());
            updateFale = sysExportExcelRecordMapper.updateRecord(detailsByBIdOne.getPactFilePath(), detailsByBIdOne.getPactFileStatus(), detailsByBIdOne.getUpdateUserId(), detailsByBIdOne.getId());
            log.info("更新导出的Excel文件是否成功>>>>{}", updateFale);
        }
        log.info("OSS上传失败情况>>>>>>>" + objectRestResponse.getMessage());
    }

    /**
     * 公共方法,获取输出流
     *
     * @param workbook
     * @param path
     * @return
     */
    private OutputStream getOutputStream(Workbook workbook, String path) {
        OutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(path);
            workbook.write(outputStream);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.close();
                workbook.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return outputStream;
    }
}
FileItemUtil工具类
package com.sgi.finance.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * @Author: LQY
 * @Description:
 * @Modified by:
 */
@Component
@Slf4j
public class FileItemUtil {

    /**
     * 通过文件路径转为FileItem
     *
     * @param filePath
     * @return
     */
    public FileItem createFileItem(String filePath,String fileName) {
        FileItemFactory factory = new DiskFileItemFactory(16, null);
        String textFieldName = "file";
        int num = filePath.lastIndexOf(".");
        String extFile = filePath.substring(num);
        FileItem item = factory.createItem(textFieldName, "multipart/form-data", true,
                fileName + extFile);

        commMethod(filePath, item);
        return item;
    }

    /**
     * 公共转换方法
     *
     * @param filePath
     * @param item
     */
    private void commMethod(String filePath, FileItem item) {
        File newfile = new File(filePath);
        int bytesRead = 0;
        byte[] buffer = new byte[8192];
        try {
            FileInputStream fis = new FileInputStream(newfile);
            OutputStream os = item.getOutputStream();
            while ((bytesRead = fis.read(buffer, 0, 8192))
                    != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

 

  • 0
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Boot提供了异步编程的支持,我们可以使用它来实现异步批量发送Http请求数据至服务端。具体步骤如下: 1. 创建一个异步的RestTemplate Bean ``` @Bean public RestTemplate asyncRestTemplate() { HttpComponentsAsyncClientHttpRequestFactory factory = new HttpComponentsAsyncClientHttpRequestFactory(); factory.setConnectTimeout(5000); factory.setReadTimeout(5000); return new RestTemplate(factory); } ``` 2. 使用异步RestTemplate发送Http请求 ``` @Autowired private RestTemplate asyncRestTemplate; public ListenableFuture<ResponseEntity<String>>[] send(List<String> urls, List<String> payloads) { ListenableFuture<ResponseEntity<String>>[] futures = new ListenableFuture[urls.size()]; for (int i = 0; i < urls.size(); i++) { String url = urls.get(i); String payload = payloads.get(i); HttpEntity<String> entity = new HttpEntity<>(payload); futures[i] = asyncRestTemplate.postForEntity(url, entity, String.class); } return futures; } ``` 3. 使用CompletableFuture处理异步请求结果 ``` List<String> urls = Arrays.asList("http://localhost:8080/api/1", "http://localhost:8080/api/2"); List<String> payloads = Arrays.asList("payload1", "payload2"); ListenableFuture<ResponseEntity<String>>[] futures = send(urls, payloads); CompletableFuture.allOf(futures).join(); for (ListenableFuture<ResponseEntity<String>> future : futures) { ResponseEntity<String> response = future.get(); // 处理响应结果 } ``` 这样就可以实现异步批量发送Http请求数据至服务端了。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值