Spring Cloud Feign Client 实现MultipartFile上传文件功能

  由于公司运用的技术栈为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格式有点不对,需要的自己修改吧

猜你喜欢

转载自blog.csdn.net/weixin_39715061/article/details/80231198