SpringBoot2实践系列(五十七):导出Excel文件并使用RestTemplate上传

star2017 1年前 ⋅ 947 阅读

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

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

上传

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

导入

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

导出

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

需求

导出订单数据到 Excel 表

实现

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

  1. 添加依赖

    <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 列名对应的实体类

    @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. 业务实现

    @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 配置

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

    @Data
    @Accessors(chain = true)
    @Configuration
    @ConfigurationProperties(prefix = "file.server")
    public class FileServerProperties {
    
        /**
         * url
         */
        private String url;
        /**
         * serverName
         */
        private String serverName;
    }
    

    配置文件属性:

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

压缩

压缩打包上传

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);
}

下载压缩文件

@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. 业务实践系列(八):对账-银联行业支付前置平台交易账单数据解析
更多内容请访问:IT源点

相关文章推荐

全部评论: 0

    我有话说: