前两天开发一个功能中涉及文件上传/下载功能,这是非常常见的一个功能,一般都是前端通过multipart/form-data的模式传递给后端,后端则通过MultipartFile接收即可。
但这次遇到一个问题,前端请求先到C端(所谓C端可以理解为服务端的入口层,所有请求都会先到达C端,再由C端下发给各种不同的服务,这样的好处是对于前端来说,不需要跟一堆的后端部门对接,只要跟C端对接就好了)
我们服务间的调用都是通过feign这个http框架,这也是跟随spring cloud一起火起来的一个框架。但也就是这个框架导致从C端通过feign透传调用下游的服务一直失败,各种问题。以往别的同事都是在C端直接把文件存到阿里的oss,然后获取文件的url,再把url传给下游服务,下游服务再通过url从阿里oss下载后再处理。
但我觉得这样做太复杂了,C端就不应该有额外的业务逻辑,尽量透传是最好的,所以一心想直接通过feign传文件。
当尝试了多种写法都无济于事的时候,突然想到百度的图片识别API不是把图片转成base64字符串传递给后台的嘛,于是我就想在C端通过MultipartFile接收后,
转成byte数组,再将byte数组转成base64字符串,再通过feign传base64字符串就毫无问题了。同样的下载文件也是如此,先从下游服务生成文件后,先转成base64字符串返回给C端,
C端再转成文件直接通过httpServletResponse输出文件流。
这是C端将MultipartFile转成base64的代码段,关键是把文件名和类型都传递下去了,下游生成MultipartFile的时候需要
````java
@PostMapping("/upload")
public ResultContentResponse<Long> upload(@RequestParam("file") MultipartFile file, @RequestParam("scene") String scene, @RequestParam("orgId") Integer orgId) throws IOException {
BankCardValideUploadRequest req = new BankCardValideUploadRequest();
//String encode = new BASE64Encoder().encode(file.getBytes());
String encode = Base64.encodeBase64String(file.getBytes());
req.setFile(encode);
req.setScene(scene);
req.setOrgId(orgId);
req.setFileContentType(file.getContentType());
req.setFileName(file.getOriginalFilename());
BasicResponse<Long> upload = bankCardValidClient.upload(req);
}
````
这是下游的代码,把base64转回MultipartFile,BASE64DecodedMultipartFile是自己实现一个MultipartFile, 需要用到文件名和类型
````java
byte[] bytes = Base64.decodeBase64(request.getFile());
MultipartFile multipartFile = new BASE64DecodedMultipartFile(bytes, request.getFileName(), request.getFileContentType());
````
````java
public class BASE64DecodedMultipartFile implements MultipartFile {
private byte[] imgContent;
private String fileName;
private String contentType;
public BASE64DecodedMultipartFile(byte[] imgContent, String fileName, String contentType) {
this.imgContent = imgContent;
this.fileName = fileName;
this.contentType = contentType;
}
@Override
public String getName() {
return fileName;
}
@Override
public String getOriginalFilename() {
return fileName;
}
@Override
public String getContentType() {
return contentType;
}
@Override
public boolean isEmpty() {
return imgContent == null || imgContent.length == 0;
}
@Override
public long getSize() {
return imgContent.length;
}
@Override
public byte[] getBytes() throws IOException {
return imgContent;
}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(imgContent);
}
@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
new FileOutputStream(dest).write(imgContent);
}
}
````
下载的时候将excel文件转成ByteArrayOutputStream
````java
ByteArrayOutputStream byteArrayOutputStream = ee.writeFileToByte();
String encode = Base64.encodeBase64String(byteArrayOutputStream.toByteArray());
result.setFile(encode);
result.setFileName(fileName);
````
再由C端输出excel
````java
@PostMapping("/result/download")
public void reslutDownload(@RequestBody @Valid BankCardDoValideRequest request, HttpServletRequest httpServletRequest, HttpServletResponse response) throws Exception {
BasicResponse<BankCardDoValideDto> bankCardDoValideDtoBasicResponse = bankCardValidClient.reslutDownload(request);
BankCardDoValideDto data = bankCardDoValideDtoBasicResponse.getData();
OutputStream os = null;
try {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("UTF-8");
response.setDateHeader("Expires", 0L);
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
response.setHeader("Pragma", "no-cache");
if (httpServletRequest.getHeader("User-Agent").toUpperCase().indexOf("MSIE") > 0) {
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(data.getFileName(), "UTF-8"));
} else {
response.setHeader("Content-Disposition", "attachment;filename*=utf-8'zh_cn'" + URLEncoder.encode(data.getFileName(), "UTF-8"));
}
os = response.getOutputStream();
byte[] bytes = Base64.decodeBase64(data.getFile());
Workbook wb = WorkbookFactory.create(new ByteArrayInputStream(bytes));
wb.write(os);
} finally {
if (os != null) {
os.flush();
os.close();
}
}
}
````