使用 Zuul 代理微服务的时候实际上也可以进行上传微服务的代理。
1.建立上传微服务

1.1、建立上传微服务

1、 建议通过之前的项目随意做一个简单的复制,复制一个新的项目:microcloud-provider-upload-8201,在这个项目之中不再需要任何的 MyBatis 操作等等,所以将所有无用的配置全部删除掉;
2、 【microcloud-provider-upload-8201】修改 application.yml 配置文件,对上传文件做一些基础配置。

server:
  port: 8201
spring:
  http:
    multipart:
      enabled: true   # 启用http上传处理
      max-file-size: 100MB # 设置单个文件的最大长度
      max-request-size: 100MB # 设置最大的请求文件的大小
      file-size-threshold: 1MB  # 当上传文件达到1MB的时候进行磁盘写入
      location: /  # 上传的临时目录
  application:
    name: microcloud-provider-upload

eureka:
  client: # 客户端进行Eureka注册的配置
    service-url:
      defaultZone: http://localhost:8081/eureka/
  instance:
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
    lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
    instance-id: dept-8001.com  # 在信息列表时显示主机名称
    prefer-ip-address: true     # 访问的路径变为IP地址

info:
  app.name: microcloud-provider-upload
  company.name: com.alen
  build.artifactId: $project.artifactId$
  build.version: $project.verson$

3、 【microcloud-provider-upload-8201】建立上传的 Rest 服务配置:
 

@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 "mldn-file-" + System.currentTimeMillis() + ".jpg" ;
   }
   public String uploadFallback(@RequestParam("photo") MultipartFile photo) {
      return "nophoto.jpg" ;
   }
}

现在实现的上传处理操作与之前讲解 SpringBoot 的形式是几乎完全一样的。

 

4、 如果要进行上传测试,可以简单一些,使用 curl 的命令完成,当然,这个命令是需要单独配置的;为了方便配置修改 hosts 配置文件,追加一个访问映射路径:

127.0.0.1 upload.com
· 使用 curl 上传语法:curl -F "参数名称=@文件本地路径" 上传地址;
curl -F "photo=@dog.jpg" http://mldnjava:hello@upload.com:8201/upload
这个时候如果你的 csrf 跨站请求配置被关闭了,则会出现如下的错误提示信息:
{"timestamp":1499667489398,"status":403,"error":"Forbidden","message":"Could  not  verify  the  provided  CSRF  token  because  your session was not found.","path":"/upload"}
跨站的访问如果在安全前提下是一定要被禁止的,但是如果牵扯到了微服务的架构问题,那么这个时候就不能够禁止了。

 

5、 【microcloud-security】修改安全策略,将 csrf 禁用;
// 表示所有的访问都必须进行认证处理后才可以正常进行
http.httpBasic().and().authorizeRequests().anyRequest()
.fullyAuthenticated().and().csrf().disable();
此时表示取消了 csrf 的验证操作,那么就证明当前的微服务允许跨站访问。那么这个时候文件就可以正常进行上传处理了。


2.通过 Zuul 代理上传微服务
现在已经实现了一个上传的微服务操作,但是现在的微服务是客户端直接调用了指定的微服务的信息来操作的,从实际的开发来讲,所有的微服务应该都被 zuul 进行代理上传。
1、 【microcloud-zuul-gateway-9501】修改 application.yml 配置文件,追加上传微服务的代理映射:

server:
  port: 9501

zuul:
  prefix: /mldn-proxy
  ignored-services:
    "*"
  routes:
    microcloud-provider-company: /company-proxy/**
    microcloud-provider-dept: /dept-proxy/**
    microcloud-provider-upload: /upload-proxy/**


eureka:
  client: # 客户端进行Eureka注册的配置
    service-url:
      defaultZone: http://localhost:8081/eureka/
  instance:
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
    lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
    instance-id: gateway-9501.com  # 在信息列表时显示主机名称
    prefer-ip-address: true     # 访问的路径变为IP地址

info:
  app.name: microcloud-zuul-gateway
  company.name: com.alen
  build.artifactId: $project.artifactId$
  build.version: $project.verson$

spring:
  application:
    name: microcloud-zuul-gateway

修改完成之后来启动 zuul 的代理微服务;

 

2、 既然现在 zuul 已经正常启动了,那么下面将利用 zuul 作为上传微服务的代理操作,于是命令上可以使用 curl 命令进行上传配置,路径:curl -F "photo=@dog.jpg" http://zdmin:mldnjava@gateway-9501.com:9501/mldn-proxy/upload-proxy/upload。

3、 【microcloud-provider-upload-8201】现在对于当前的上传微服务已经设置好了一个允许的上传文件大小:100M,于是现在上传一个 16M 的大小的文件,访问路径:
curl -F "photo=@my.jpg" http://zdmin:mldnjava@gateway-9501.com:9501/mldn-proxy/upload-proxy/upload

错误信息:

{"timestamp":1499742379394,"status":400,"error":"Bad Request","exception":"org.eclipse.jetty.http.BadMessageException","message":"Unable to parse form content", "path":"/mldn-proxy/upload-proxy/upload"}

4、 现在通过SpringCloud的官方文档可以发现,在文档之中已经明确要求了,在上传大文件的时候,那么就必须明确的将zuul中的上传控制交由处理的微服务来进行,所以应该在访问路径前追加有“/zuul/**”映射路径。
curl -F "photo=@my.jpg" http://zdmin:mldnjava@gateway-9501.com:9501/zuul/mldn-proxy/upload-proxy/upload
那么此时就表示当前的上传的代理操作,zuul 不再进行限制,而是直接交由目的微服务进行。

 

5、 【microcloud-zuul-gateway-9501】修改 application.yml 配置文件,进行超时时间的配置:

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 60000
ribbon:
  ConnectTimeout: 3000
  ReadTimeout: 60000

由于上传的文件较大,所以需要进行超时时间的配置,才可以正常实现上传。

 
3.客户端调用上传微服务

现在为止所有的上传微服务虽然可以调用了,但是却使用的是 curl 命令完成的,很明显,这样的操作根本就不可能在实际的开发之中使用,那么如果要想使用,肯定要通过表单来完成上传处理,也就是说这个时候应该会有一个客户端(WEB 端、SpringBoot实现)调用 zuul,而后再由 zuul 去代理上传微服务。

 于是这个时候大家肯定首先想到的就是 Feign,但是非常遗憾的告诉大家,对于上传的这种微服务的操作是无法利用 Feign 做接口转换,也无法直接使用 RestTemplate 做代理操作。也就是说此时如果要想调用,那么唯一的方案就是利用 httpclient 完成。

 

1、 建立一个新的客户端项目:microcloud-consumer-upload,而后这个项目主要是运行 SpringBoot 程序,同时要结合 thymeleaf 作为页面显示;


2、 【microcloud-consumer-upload】修改 pom.xml 配置文件,这个配置文件里面需要追加 httpclient 的两个依赖包:

<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>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

 

3、 【microcloud-consumer-upload】修改 application.yml 配置文件,在这个配置文件里面追加有如下的配置项:
 

server:
  port: 8093
eureka:
  client: # 客户端进行Eureka注册的配置
    register-with-eureka: false # 不在eureka之中进行注册
    service-url:
      defaultZone: http://localhost:8081/eureka/
  instance:
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
    lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
    instance-id: upload.com  # 在信息列表时显示主机名称
    prefer-ip-address: true     # 访问的路径变为IP地址


http://edmin:mldnjava@eureka-7002.com:7002/eureka,
http://edmin:mldnjava@eureka-7003.com:7003/eureka

 

4、 【microcloud-consumer-upload】建立一个 src/main/view 的目录,在这个目录里面主要是将其提升为源文件目录,随后将所需要的页面文件设置到此目录之中,并且在里面建立 static、templates 两个子目录;


5、 【microcloud-consumer-upload】建立 src/main/view/templates/upload.html 页面;

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>SpringCloud微服务</title>
<link rel="icon" type="image/x-icon" href="/images/mldn.ico" />
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
</head>
<body>
   <form th:action="@{/consumer/upload}" method="post" enctype="multipart/form-data">
      姓名:<input type="text" name="name" id="name" value="MLDN"/><br/>
      照片:<input type="file" name="photo" id="photo"/><br/>
      <input type="submit" value="提交"/>
      <input type="reset" value="重置"/>
   </form>
</body>
</html>


6、 【microcloud-consumer-upload】建立一个控制层的程序代码。实现上传微服务的调用;
 

@Controller
public class ConsumerUploadController {
   // 设置要进行远程上传微服务调用的代理地址
   public static final String UPLOAD_URL = "http://localhost:9501/zuul/mldn-proxy/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对象
         HttpClientContext httpContext = HttpClientContext.create(); // 创建Http处理操作的上下文对象
         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";
   }
}


 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐