工作中一直使用的是SpringCloud,其中的多个组件的使用也已经有一段时间了,包括对组件的配置文件的编写,今天要讲的是SpringCloud中的一个远程服务调用的组件,使用Feign之后,我们调用Eureka注册的其他服务,在代码中就像各个service之间相互调用那么简单。那至于为什么要调用其他服务的接口,就是微服务架构的内容了,今天我讲的是我们项目中独立出来的文件上传模块,我们在项目中将其做成一个通用的接口,使得其他服务中有相同需求时,可以直接调用该服务的接口进行文件上传以及文件管理。
Feign简介
Feign是Netflix开发的声明式,模板化的HTTP客户端,其灵感来自Retrofit,JAXRS-2.0以及WebSocket。
- Feign可帮助我们更加便捷,优雅的调用HTTP API。
- 在SpringCloud中,使用Feign非常简单——创建一个接口,并在接口上添加一些注解,代码就完成了。
- Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等。
- SpringCloud对Feign进行了增强,使Feign支持了SpringMVC注解,并整合了Ribbon和Eureka,从而让Feign的使用更加方便。
文件上传服务
文件上传我们在项目中将其单独作为一个服务使用,这是考虑到系统中有其他不同的模块需要使用到文件上传,因此独立出来,能够避免在各个子模块中重复编写文件上传的代码,也提高了文件上传接口的使用率。创建文件上传服务的过程就不再介绍了,文件上传想必大家做的都非常多了,我们将主要接口暴露出来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
@Controller @RequestMapping("/UploadAppendixCommonController") public class UploadAppendixCommonController extends BaseController {
private Logger logger = LoggerFactory.getLogger(UploadAppendixCommonController.class); @Autowired HttpServletRequest request; @Autowired HttpServletResponse response;
@Autowired private SessionUtils sessionUtils;
@Autowired private UploadAppendixCommonService uploadAppendixCommonService;
@Autowired private UploadAppendixCommonConf uploadAppendixCommonConf;
@RequestMapping("/uploadAppendixToServer4Feign") @ResponseBody public String uploadAppendixToServer4Feign(@RequestPart("file") MultipartFile file) throws Exception{ List<String> fileNames = new ArrayList<String>(); String fileName = null; try { String uuid = UUID.randomUUID().toString().replaceAll("-", "");
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd"); String time = df.format(new Date());
File uploadfile = new File(uploadAppendixCommonConf.getPath()+time); if (!uploadfile.exists()) { uploadfile.mkdirs(); } if (file != null) { fileName = file.getOriginalFilename(); String path = uploadAppendixCommonConf.getPath() + time + "/" + uuid + fileName.substring(fileName.length()-5,fileName.length()); File localFile = new File(path); file.transferTo(localFile); fileNames.add(fileName); } String appendixPath = uploadAppendixCommonConf.getPath()+time+"/"; uploadAppendixCommonService.uploadAppendixToServer(uuid,fileNames,null,appendixPath);
return uuid; }catch (Exception e){ logger.error("UploadAppendixCommonController.uploadAppendixToServer抛出了异常: ", e.getMessage() + e); throw new Exception(e); } }
@RequestMapping("/downloadFileByUUID4Feign") @ResponseBody public void downloadFileByUUID4Feign(@RequestParam("UUID") String uuid) throws Exception{ Writer out = null; try { response.setHeader("Connection", "close"); response.setContentType("multipart/form-data"); DataModel dataModel = uploadAppendixCommonService.getFileName(uuid); String fileName = dataModel.get("APPENDIX_NAME").toString(); String filePath = dataModel.get("APPENDIX_PATH").toString(); String tempFileName = ""; try { tempFileName = ExportUtil2.encodeFileName(request, fileName); }catch (Exception e){ logger.error("UploadAppendixCommonController.downloadFileByUUID抛出了异常: ", e.getMessage() + e); } response.setHeader("Content-Disposition", "attachment;filename=" + tempFileName);
File file = new File(filePath); if (file.exists()){ byte[] buffer = new byte[1024]; FileInputStream fis = null; BufferedInputStream bis = null; try { fis = new FileInputStream(file); bis = new BufferedInputStream(fis); OutputStream os = response.getOutputStream(); int i = bis.read(buffer); while (i != -1) { os.write(buffer, 0, i); i = bis.read(buffer); } } catch (Exception e) { e.printStackTrace(); } finally { if (bis != null) { try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } }catch (Exception e){ logger.error("UploadAppendixCommonController.downloadFileByUUID抛出了异常: ", e.getMessage() + e); throw new Exception(e); } }
@RequestMapping("/deleteFileByUUID") @ResponseBody public String deleteFileByUUID(@RequestParam("UUID")String uuid) throws Exception{ try { return uploadAppendixCommonService.deleteFileByUUID(uuid); }catch (Exception e){ logger.error("UploadAppendixCommonController.deleteFileByUUID抛出了异常: ", e.getMessage() + e); throw new Exception(e); } } }
|
值得注意的一点是,需要在SpringCloud的启动程序中加入Feign的注解。
1 2 3 4 5 6 7 8 9 10 11
| @SpringBootApplication @EnableEurekaClient @EnableFeignClients @EnableSwagger2 public class BusinessFileManageSystemApplication {
public static void main(String[] args) { SpringApplication.run(BusinessFileManageSystemApplication.class, args); }
}
|
对文件上传服务的调用
文件上传服务配置完成之后,我们进行该服务的调用,首先需要在项目中配置接口,用作在整个项目中进行调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| @FeignClient(name = "business-file-manage-system",configuration = FeignMultipartSupportConfig.class) public interface UploaderServiceFeignClient { @RequestMapping(value="/UploadAppendixCommonController/uploadAppendixToServer4Feign", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public String uploadAppendixToServer(@RequestPart("file") MultipartFile file); @RequestMapping(value="/UploadAppendixCommonController/downloadFileByUUID4Feign") public Response downloadFileByUUID4Feign(@RequestParam("UUID") String uuid);
@RequestMapping(value = "/UploadAppendixCommonController/downloadFileByUUID",method = RequestMethod.GET) Response downloadFileByUUID(@RequestParam("UUID")String uuid);
@RequestMapping(value = "/UploadAppendixCommonController/deleteFileByUUID") String deleteFileByUUID(@RequestParam("UUID")String uuid);
public class UploaderServiceFallback implements UploaderServiceFeignClient{ public String uploadAppendixToServer(MultipartFile file) { return ResponseEntity.error("上传服务断线,请稍后重试!"); } public Response downloadFileByUUID4Feign(String uuid) { throw new RuntimeException("上传服务断线,请稍后重试!"); }
@Override public Response downloadFileByUUID(String uuid) { throw new RuntimeException("上传服务断线,请稍后重试!"); }
@Override public String deleteFileByUUID(String uuid) { return ResponseEntity.error("上传服务断线,请稍后重试!"); }
} }
|
编写完interface后,在调用处需要将接口实例注入,然后通过接口实例调用接口中的方法,我们以调用文件上传为例,下面提供代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
@RequestMapping("/uploadAppendixToServer") @ResponseBody public String uploadAppendixToServer(HttpServletRequest request, HttpServletResponse response) throws Exception{ Writer out = null; List<String> fileNames = new ArrayList<String>(); String fileName = null; String id = request.getParameter("id"); String uuid = null; try { CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver( request.getSession().getServletContext()); if (multipartResolver.isMultipart(request)) { MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request; Iterator<String> iter = multiRequest.getFileNames(); while (iter.hasNext()) { MultipartFile file = multiRequest.getFile(iter.next()); if (file != null) { uuid = uploaderServiceFeignClient.uploadAppendixToServer(file); } } } uploadAppendixCommonService.updateAppendixNameListId(id,uuid);
return ResponseEntity.success(uuid,"success"); }catch (Exception e){ logger.error("UploadAppendixCommonController.uploadAppendixToServer抛出了异常: ", e.getMessage() + e); throw new Exception(e); } }
|
其实整个过程并不复杂,可以看到我们在配置完Feign之后,调用接口就像调用普通的service一样,同时通过Feign将模块与模块之间联系起来,后续SpringCloud其他组件我们还会再做介绍.