Spring Cloud Feign Client 实现MultipartFile上传文件功能
由于公司运用的技术栈为Spring Cloud(一些Eureka, Feign)进行服务注册和远程调用。需要上传头衔这个需求but,重点来了,但直接使用FeignClient去远程调用注册中心上的上传文件接口,会一直报错。好吧我们先来捋一下架构: 由于上传的功能是使用表单来完成上传处理,也就是说这个时候应该会有一个客户端(WEB 端、SpringBoot实现)调用 zuul,而后再由 zuu
由于公司运用的技术栈为Spring Cloud(一些Eureka, Feign)进行服务注册和远程调用。需要上传头衔这个需求but,重点来了,但直接使用FeignClient去远程调用注册中心上的上传文件接口,会一直报错。好吧我们先来捋一下架构:
由于上传的功能是使用表单来完成上传处理,也就是说这个时候应该会有一个客户端(WEB 端、SpringBoot实现)调用 zuul,而后再由 zuul 去代理上传微服务.
于是这个时候大家肯定首先想到的就是 Feign,但是非常遗憾的告诉大家,对于上传的这种微服务的操作是无法利用 Feign 做接口转换,也无法直接使用 RestTemplate 做代理操作。也就是说此时如果要想调用,那么唯一的方案就是利用 httpclient 完成。
首先我们在客户端上面,运行 SpringBoot 程序,同时要结合 thymeleaf 作为页面显示;
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpmime</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
在前台建立控制层:
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class ConsumerUploadController { @RequestMapping(value = "/consumer/uploadPre", method = RequestMethod.GET) public String uploadPre() { return "upload"; } @RequestMapping(value = "/consumer/upload", method = RequestMethod.POST) public @ResponseBody String upload(String name, MultipartFile photo) { if (photo != null) { return "【*** 消费端 ***】name = " + name + "、photoName = " + photo.getOriginalFilename() + "、ContentType = " + photo.getContentType(); } return "nophoto.jpg"; } }
通过 httpclient 进行远程调用:
import java.nio.charset.Charset; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.auth.AuthScope; import org.apache.http.auth.Credentials; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; @Controller public class ConsumerUploadController { // 设置要进行远程上传微服务调用的代理地址 public static final String UPLOAD_URL = "http://xxxxxxxxx:9501/zuul/xxxxxx/upload-proxy/upload"; @RequestMapping(value = "/consumer/uploadPre", method = RequestMethod.GET) public String uploadPre() { return "upload"; } @RequestMapping(value = "/consumer/upload", method = RequestMethod.POST) public @ResponseBody String upload(String name, MultipartFile photo) throws Exception { if (photo != null) { CloseableHttpClient httpClient = HttpClients.createDefault(); // 创建一个HttpClient对象 CredentialsProvider credsProvider = new BasicCredentialsProvider(); // 创建了一个具有认证访问的信息 Credentials credentials = new UsernamePasswordCredentials("zdmin", "mldnjava"); // 创建一条认证操作信息 credsProvider.setCredentials(AuthScope.ANY, credentials); // 现在所有的认证请求都使用一个认证信息 HttpClientContext httpContext = HttpClientContext.create(); // 创建Http处理操作的上下文对象 httpContext.setCredentialsProvider(credsProvider);// 设置认证的提供信息 HttpPost httpPost = new HttpPost(UPLOAD_URL); // 设置要进行访问的请求地址 HttpEntity entity = MultipartEntityBuilder.create() .addBinaryBody("photo", photo.getBytes(), ContentType.create("image/jpeg"), "temp.jpg") .build(); httpPost.setEntity(entity); // 将请求的实体信息进行发送 HttpResponse response = httpClient.execute(httpPost, httpContext) ; // 执行请求的发送 return EntityUtils.toString(response.getEntity(),Charset.forName("UTF-8")) ; // return "【*** 消费端 ***】name = " + name + "、photoName = " // + photo.getOriginalFilename() + "、ContentType = " // + photo.getContentType(); } return "nophoto.jpg"; } }
这样伪造了一个 Http 协议的 POST 请求,才将图片传递到了上传微服务之中。
简单的就是这样,这是我们客户端的代码,由于废了挺长时间希望能对别人有所帮助
这片代码是服务器端的,同时也配置了熔断(只不过我改成了简单的输出而不是掉图片服务器):
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; @RestController public class UploadRest { @RequestMapping(value = "/upload", method = RequestMethod.POST) @HystrixCommand(fallbackMethod="uploadFallback") public String upload(@RequestParam("photo") MultipartFile photo) { if (photo != null) { // 表示现在已经有文件上传了 System.out.println("【*** UploadRest ***】文件名称:" + photo.getOriginalFilename() + "、文件大小:" + photo.getSize()); } return "-" + System.currentTimeMillis() + ".jpg" ; } public String uploadFallback(@RequestParam("photo") MultipartFile photo) { return "nophoto.jpg" ; }
现在通过看 SpringCloud 的官方文档可以发现,在文档之中已经明确要求了,在上传大文件的时候,那么就必须明确的将 zuul中的上传控制交由处理的微服务来进行,所以应该在访问路径前追加有“/zuul/**”映射路径。
so:
hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 60000 ribbon: ConnectTimeout: 3000 ReadTimeout: 60000
由于上传的文件较大,所以需要进行超时时间的配置,才可以正常实现上传。
特别是赋值配置文件的时候,yml格式有点不对,需要的自己修改吧
更多推荐
所有评论(0)