Spring Cloud Feign Client implements MultipartFile upload file function

  Since the technology stack used by the company is Spring Cloud (some Eureka, Feign) for service registration and remote invocation. There is a need to upload the title but, the point is here, but directly using FeignClient to remotely call the upload file interface on the registration center will always report an error. Well, let's take a look at the architecture first:

  Since the upload function is to use the form to complete the upload process, that is to say, at this time, there should be a client (WEB side, SpringBoot implementation) calling zuul, and then zuul will proxy the upload of the microservice.

  So at this time, the first thing that everyone thinks of is Feign, but I regret to tell you that Feign cannot be used for interface conversion, nor can RestTemplate be used directly for proxy operation. That is to say, if you want to call at this time, the only solution is to use httpclient to complete.

First of all, we run the SpringBoot program on the client, and at the same time, combine thymeleaf as a page display;

<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>

Create a control layer in the foreground:

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";
        }
    }

Remote call via 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格式有点不对,需要的自己修改吧

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325950532&siteId=291194637