项目场景:
接手一个老的项目,项目中整合了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