Spring Boot 2系列(五十六):导出Excel文件并使用RestTemplate上传

微服务平台在开发网关时没考虑小文件的上传和下载,而是直接由交文件服务处理,返回访问文件的URL给业务。

现在业务需要上传导入和导出下载,因所有后端服务接口请求必须走网关,上传和下载就必须经过文件服务来处理。

上传

由页面直接请求文件服务上传文件,文件服务返回访问文件的URL或GUID给业务。

导入

前端请求文件服务上传文件,文件服务返回URL或GUID,前端拿到 URL或GUID和其它参数一起请求业务后端,业务后端下载文件执行导入操作。

导出

将业务数据写出到文件(临时文件),读取文件上传到文件服务,拿到返回的 URL 或 GUID去下载。

需求

导出订单数据到 Excel 表

实现

导出使用 EasyPoi 工具,快捷方便。

  1. 添加依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
    </dependency>
    <dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-base</artifactId>
    <version>4.1.0</version>
    </dependency>
    <dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-web</artifactId>
    <version>4.1.0</version>
    </dependency>
    <dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-annotation</artifactId>
    <version>4.1.0</version>
    </dependency>
  2. 与 Excel 列名对应的实体类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    @Data
    @Accessors(chain = true)
    public class OrderExportBO {

    /**
    * 下单时间
    */
    @Excel(name = "下单时间", width = 20)
    private String createdTime;
    /**
    * 送餐日期
    */
    @Excel(name = "配送日期", width = 20)
    private String deliveryDate;

    /**
    * 收件人
    */
    @Excel(name = "姓名", width = 15)
    private String consignee;
    /**
    * 手机号
    */
    @Excel(name = "手机号", width = 15)
    private String mobile;
    /**
    * 地址
    */
    @Excel(name = "地址", width = 20)
    private String address;
    /**
    * 金额
    */
    @Excel(name = "金额", width = 10)
    private BigDecimal totalFee;
    /**
    * 备注
    */
    @Excel(name = "备注", width = 30)
    private String remark;
    }
  3. 业务实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    @Service
    public class ExportServiceImpl implements ExportService {
    private static final Logger logger = LogManager.getLogger(ExportServiceImpl.class);

    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private FileServerProperties fileServerProperties;

    /**
    * @desc: 导出订单
    * @param: [orderVO]
    */
    @Override
    public ResponseModel<?> exportOrder(OrderVO orderVO) {
    // 查询订单数据
    List<BzOrder> orderList = this.queryOrderList(orderVO);
    // 将订单数据包装成要导出的数据
    List<OrderExportBO> exportList = this.wrapExportData(orderList);
    // EasyPoi导出
    ExportParams exportParams = new ExportParams();
    exportParams.setType(ExcelType.XSSF);
    Workbook workbook = ExcelExportUtil.exportExcel(exportParams, OrderExportBO.class, exportList);
    // 字节数组输出流
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    ResponseModel<?> responseModel = null;
    try {
    workbook.write(out);
    out.flush();
    out.close();
    workbook.close();
    // 字节数组输入流
    ByteArrayInputStream bio = new ByteArrayInputStream(out.toByteArray());
    // 创建临时文件
    File file = File.createTempFile("导出订单信息", ".xlsx");
    FileUtils.copyInputStreamToFile(bio, file);
    // 上传
    responseModel = this.uploadFile(file);
    } catch (IOException e) {
    e.printStackTrace();
    logger.error("导出订单信息异常:{}", e.getMessage());
    throw new BusinessException(e);
    }
    return responseModel;
    }

    /**
    * @desc: 上传文件
    * @param: [file]
    */
    private ResponseModel<?> uploadFile(File file) {
    // 设置请求头
    HttpHeaders headers = new HttpHeaders();
    // MediaType type = MediaType.parseMediaType(MediaType.MULTIPART_FORM_DATA_VALUE);
    // headers.setContentType(MediaType.MULTIPART_FORM_DATA);
    MediaType type = MediaType.parseMediaType("multipart/form-data");
    headers.setContentType(type);
    FileSystemResource resource = new FileSystemResource(file);
    // 设置请求体,注意是LinkedMultiValueMap
    MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
    form.add("file", resource);
    form.add("serverName", fileServerProperties.getServerName());

    // 用HttpEntity封装整个请求报文
    HttpEntity<MultiValueMap<String, Object>> files = new HttpEntity<>(form, headers);
    // RestTemplate
    ResponseModel<?> response = restTemplate.postForObject(fileServerProperties.getUrl(), files, ResponseModel.class);
    return response;
    }
    }

  4. RestTemplate 配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Configuration
    public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() {
    RestTemplate template = new RestTemplateBuilder()
    .setConnectTimeout(Duration.ofMillis(30000))
    .setReadTimeout(Duration.ofMillis(30000))
    .build();
    return template;
    }
    }
  5. 属性配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Data
    @Accessors(chain = true)
    @Configuration
    @ConfigurationProperties(prefix = "file.server")
    public class FileServerProperties {

    /**
    * url
    */
    private String url;
    /**
    * serverName
    */
    private String serverName;
    }

    配置文件属性:

    1
    2
    3
    #===========File Server============
    file.server.url=http://file-server.domain.com/upload
    file.server.serverName=xxxxxxxxxx

压缩

压缩打包上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
ByteArrayOutputStream zipos = new ByteArrayOutputStream(61858764);//设置大小为60M
ZipOutputStream zos = new ZipOutputStream (zipos) ;//创建压缩流,初始化一个输出流缓存区
for(Entry<String, JSONObject> entry : mapData.entrySet()){
String key = entry.getKey();
JSONObject jsonVal = entry.getValue();
OrderMonitorExcelViewSvc orderMonitorExcelView = new OrderMonitorExcelViewSvc(jsonVal);
try {
orderMonitorExcelView.outputToFile("orderMonitorExport_"+key);
} catch (Exception e1) {
logger.info(e1);
}
HSSFWorkbook workbook = orderMonitorExcelView.getWorkbook();
ByteArrayOutputStream os = new ByteArrayOutputStream(61858764);//设置大小为60M
try {
workbook.write(os);
//创建一个压缩文件里的名称
ZipEntry zipEntry = new ZipEntry("订单监控查询"+key+".xls");
System.out.println(os.size());
zipEntry.setSize(os.size());
zipEntry.setCompressedSize(os.size());
zos.putNextEntry(zipEntry);
os.writeTo(zos);
zos.closeEntry();
os.close();
} catch (IOException e) {
logger.info("写入ZipOutputStream异常");
}
}
try {
zos.close();//注意关闭流的顺序,在上传oss之前必须关闭流否则下载解压的时候会报“文件末端错误”的问题
zipos.close();
ByteArrayInputStream zipis = new ByteArrayInputStream(zipos.toByteArray());
@SuppressWarnings("deprecation")
int rel = cpsdCoralStorage.uploadFile("order_monitor_data/"+batchno,zipis);
if(rel==0){
orderMonitorDao.updateDownloadBatch("0", batchno);
}
zipis.close();
} catch (IOException e) {
logger.info("上传oss失败");
logger.info("关闭压缩流异常"+e);
}

下载压缩文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@RequestMapping(value = { "downloadExport" })
public void downloadExport(HttpServletRequest request, HttpServletResponse response,
@RequestParam(value = "batchno", required = true) String batchno) throws IOException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String formatDate = sdf.format(new Date());
String pathName = "order_monitor_data/" + batchno;
String srcFileName = "导出结果" + formatDate + ".zip";
InputStream is = cpsdCoralStorage.downloadFile(pathName);
try {
response.setHeader("content-disposition",
"attachment;filename=" + URLEncoder.encode(srcFileName, "utf-8"));
OutputStream out = response.getOutputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int temp =0;
while((temp=is.read())!=-1){ // 读取内容
baos.write(temp);
}
byte[] xlsBytes = baos.toByteArray();
out.write(xlsBytes);
out.flush();
out.close();
}
catch (FileNotFoundException e) {
logger.logException(e);
}
catch (IOException e) {
logger.logException(e);
}
}

参考

  1. Spring Boot 2实践系列(五十六):获取 jar 包 resources 中的资源文件
  2. SpringMVC(六):文件下载,压缩打包下载
  3. 业务实践系列(八):对账-银联行业支付前置平台交易账单数据解析

Spring Boot 2系列(五十六):导出Excel文件并使用RestTemplate上传

http://blog.gxitsky.com/2020/12/31/SpringBoot-56-upload-excel/

作者

光星

发布于

2020-12-31

更新于

2022-07-18

许可协议

评论