Feign跨服务下载文件; getOutputStream() has already been called for this response

项目场景:

接手一个老的项目,项目中整合了Activiti但是没有经过正式使用,现在需要正式使用起来了但是导出Activiti在线设计的模型有问题,在代码中是A服务通过Feign调用B服务来实现的。

问题描述:

点击导出后在A服务直接报getOutputStream() has already been called for this response错误,原因是response已经被占用了,后来经过查资料发现是Feign的问题。

原因分析:

Feign在跨服务下载文件时,需要用到Feign提供的Request来做一次中继操作

解决方案:

1.客户端编码

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import feign.Response;

@RequestMapping(value = "export")
    public void downFile(String id, HttpServletResponse servletResponse) throws IOException {
        // feign文件下载
        Response response = actModelService.export(id);

        Response.Body body = response.body();
        for (Object key : response.headers().keySet()) {
            List<String> kList = (List<String>) response.headers().get(key);
            for (String val : kList) {
            //这块获取的是Feign reponse中的header,但是经过测试只能获取下面两个内容,所以在在下面就又设置了了setContentType、setCharacterEncoding、addHeader参数
            //key---date_-----value----Fri, 26 Nov 2021 09:15:43 GMT 
            //key---transfer-encoding_-----value----chunked
                servletResponse.setHeader(key.toString(), val);
            }
        }
        servletResponse.setContentType("text/xml");
        servletResponse.setCharacterEncoding("utf-8");
        servletResponse.addHeader("Content-Disposition", "attachment;filename=bpmn.xml");
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = body.asInputStream();
            outputStream = servletResponse.getOutputStream();
            byte[] b = new byte[inputStream.available()];
            inputStream.read(b);
            outputStream.write(b);
            outputStream.flush();
        } catch (IOException e) {
            System.out.println("失败了");
        }finally {
            inputStream.close();
            outputStream.close();
        }
    }

2.FeignServer中编码

import feign.Response;

    @RequestMapping(value = "/export")
    Response export(@RequestParam("id") String id);

3.服务端编码

 /**
     * 导出模型
     */
    public void export(String id,HttpServletResponse response) {
        try {
            Model model = repositoryService.getModel(id);
            BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
            JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(model.getId()));
            BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
            BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
            byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);

            ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);

            IOUtils.copy(in, response.getOutputStream());
            String filename = bpmnModel.getMainProcess().getId() + ".xml";
            response.setHeader("Content-Disposition", "attachment; filename=" + filename);
            response.flushBuffer();
        } catch (Exception e) {
            throw new RRException(LocaleMessageUtil.getMessage("actModel.export.exportError")+id);
        }
    }

4.扩展-Java中下载文件的步骤

public void fileDownload(HttpServletResponse response){
        //获取网站部署路径(通过ServletContext对象),用于确定下载文件位置,从而实现下载
        String path = servletContext.getRealPath("/");

        //1.设置文件ContentType类型,这样设置,会自动判断下载文件类型
        response.setContentType("multipart/form-data");
        //2.设置文件头:最后一个参数是设置下载文件名(假如我们叫a.pdf)
        response.setHeader("Content-Disposition", "attachment;fileName="+"a.pdf");
        ServletOutputStream out;
        //通过文件路径获得File对象(假如此路径中有一个download.pdf文件)
        File file = new File(path + "download/" + "download.pdf");

        try {
            FileInputStream inputStream = new FileInputStream(file);

            //3.通过response获取ServletOutputStream对象(out)
            out = response.getOutputStream();

            int b = 0;
            byte[] buffer = new byte[512];
            while (b != -1){
                b = inputStream.read(buffer);
                //4.写到输出流(out)中
                out.write(buffer,0,b);
            }
            inputStream.close();
            out.close();
            out.flush();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

参考文章:
FeignClient 跨服务上传文件、导出Excel
https://blog.csdn.net/qq_20919883/article/details/111144221
Java中都通用文件下载(ContentType、文件头、response、out四步骤)
https://www.cnblogs.com/yangjian-java/p/6832264.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值