一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情。
Feign上传下载
问题背景
基于目前一些主流的框架,基本上都会用到微服务的架构,很多时候会将file文件管理的功能,抽取到一个独立的微服务例如file微服务上面。
前端的请求,都统一的调用file微服务进行文件的上传和下载的功能!!!
这样的设计,好像是没啥毛病,统一管理嘛!!!
因为文件上传,可能还需要写入业务数据,例如,附件的信息等,这就不得不抽取成一个独立的微服务,供前端去使用了。
那就会有个问题呀:!!!T_T
1.如果我的附件不是通过前端上传的呢?我是一个后端的服务,需要保存一个附件,那你咋搞嘛?
2.我后端的一个服务,需要读取一个文件,进行解析,因为是分布式的架构,可能
file微服务部署到服务器A,咋们后端服务部署在服务B,这样,根据磁盘路径,根本就无法读取到文件,那你又怎么处理呢?
嗯嗯嗯,你说的都很有道理,这确实是个大问题呀!!!
别慌,咋们今天就来解决这个问题!!!
因为我们服务之间接口的调用,在微服务体系中,一般都是通过feign去调。那么文件这块,能不能也通过feign去调用呢?
答案,肯定是可以的呀,哈哈!!!^_^
可能有些同学,不太理解,为什么后端需要上传文件?
那这里咋们举个例子:
例如:有这样一个场景,你有一个
word模板文件,然后你后端业务代码的功能,将这个业务数据根据word模板文件,然后填充word模板生成一份报告或者其他文件。(该功能是后端执行的)那这个生成文件,是自己保存呢?还是交给file微服务管理保存呢?
如果你是交给
file微服务管理这个文件,那这个文件就需要上传到file微服务。这就出现了后端需要上传文件的问题。
为什么这里还多此一举将文件上传到
file微服务?1.统一文件管理,方便后期维护。
2.
file微服务有业务表,保存了文件上传记录数据备注:当然啦,这个还是得看你的业务,不强求这样做啦!!!
那废话不多说,上代码!!!
Feign接口准备
- 定义一个
Feign上传,下载的接口
@FeignClient(name = "file", configuration = {FileClient.MultipartSupportConfig.class})
public interface FileClient {
/**
* 上传文件
* @param file
* @return
*/
@RequestMapping(path = "/file/upload", consumes = {"multipart/form-data"})
JsonResult upload(@RequestPart(name="file") MultipartFile file);
/**
* 下载文件
*/
@GetMapping(path = "/file/download/{fileId}")
Response download(@PathVariable(name = "fileId") String fileId);
public class MultipartSupportConfig {
@Bean
@Primary
@Scope("prototype")
public Encoder feignFormEncoder() {
return new SpringFormEncoder();
}
}
}
file微服务提供文件上传接口
@RequestMapping("/file/upload")
public JsonResult upload(MultipartHttpServletRequest request) throws Exception {
JsonResult jsonResult = new JsonResult();
String timestamp = request.getParameter("time");
MultiValueMap<String, MultipartFile> multiFileMap = request.getMultiFileMap();
List<MultipartFile> files = multiFileMap.get("files");
if (BeanUtil.isEmpty(files)) {
files = multiFileMap.get("file");
}
Iterator<MultipartFile> it = files.iterator();
while (it.hasNext()) {
MultipartFile multipartFile = it.next();
//保存到磁盘
multipartFile.transferTo(new File("文件路径"));
//记录文件上传业务数据
...
xxxxFileDao.saveFile(xxxFile);
}
jsonResult.setSuccess(true);
jsonResult.setData(fileList);
jsonResult.setMessage("成功上传!");
return jsonResult;
}
file微服务提供文件下载接口
@GetMapping("/download/{fileId}")
public void feigndownloadOne(@PathVariable("fileId") String fileId, HttpServletResponse response) throws Exception {
//通过fileId查找文件
...
xxxFile = xxxxFileDao.getFile(fileId);
File file = new File("文件路径");
String filename = xxxFile.getName();
String type ="attachment";
response.setHeader("Content-Disposition", type + ";filename=" + filename);
FileInputStream fis = new FileInputStream(file);
BufferedInputStream buff = null;
OutputStream out = null;
try {
buff = new BufferedInputStream(fis);
out = response.getOutputStream();
IOUtils.copy(buff, out);
} finally {
out.flush();//注意out流,对象不能close
fis.close();
buff.close();
}
}
下载这个接口,有个比较重要的地方就是,out流,不能close(),否则就算获取到该流,也无法处理业务逻辑。
好了,上面就是咋们定义好的feign接口,和提供服务的file微服务上传下载接口!!!
上传下载接口,只是给出一个demo,大家伙,可自行修改,相应的业务逻辑!!!^_^
Feign上传下载
接下来,咋们就开始使用到这个feign的上传下载功能!!!
- 上传
feign接口,使用
import java.io.File;
import java.io.FileInputStream;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.mock.web.MockMultipartFile;
import org.apache.http.entity.ContentType;
File pdfFile = new File("D://test.pdf");
FileInputStream fileInputStream = new FileInputStream(pdfFile);
MultipartFile multipartFile = new MockMultipartFile(pdfFile.getName(), pdfFile.getName(),
ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);
//上传
JsonResult jsonResult = fileClient.upload(multipartFile);
if (jsonResult.isSuccess() && jsonResult.getCode() == 200) {
}
注意了 注意了 注意了!!!
这里,有个要注意的地方,因为我们的
upload接口,即controller接口一般使用MultipartFile对象接收文件,那么我们就得把File,对象先转成MultipartFile对象,才可以调通这个接口!!!转换方式,看上面代码即可!!!
好了,这个就是上传文件的示例,也是比较简单啦!!!
- 下载
feign接口,使用
import feign.Response;
//文件流
Response response = fileClient.download(fileId);
InputStream in = null;
if(response.status() == 200){
Response.Body body = response.body();
in = body.asInputStream();
//拿到这个in流,处理相应的业务逻辑
...
//处理完成后,记得关闭一下inputStream流
if(in != null){
in.close();
}
}
注意了 注意了 注意了!!!
这里有两个地方需要注意:
1.因为我们的download接口,
out流是还没有close(),所以这里的in流,用完得close()一下。2.这里的代码,在开发过程中,不能
打断点调试,不能打断点调试,不能打断点调试!!!重要的事情得说三遍,不能
打断点调试,因为你断点调试后,这里的in流,会因为执行了toString()方法,导致in流被关闭了,无法再使用,即不能处理业务逻辑代码了!!!
好了,这个就是上传文件的示例,也是比较简单啦!!!
好了,Feign上传和下载,大概的实现,就是这样了!!!^_^
那我们就可以愉快的编写代码了!!!^_^