08-微服务文件上传实战

08-微服务文件上传实战,第1张

08-微服务文件上传实战

目录

项目简介

业务描述

初始架构设计

工程创建及初始化

工程结构

创建父工程

创建文件服务工程

创建客户端服务工程

父工程初始化

文件资源服务实现

添加项目依赖

服务初始化配置

构建项目启动类

Controller逻辑实现

跨域配置实现

客户端工程逻辑实现

添加依赖

构建项目启动类

创建文件上传页面

启动服务访问测试

API网关(Gateway)工程实践

概述

服务调用架构

工程项目结构设计

创建网关工程及初始化

网关跨域配置

启动工程进行服务访问

网关上对文件上传限流

AOP方式 *** 作日志记录

页面描述

添加项目依赖

创建切入点注解

定义切入点方法

定义日志 *** 作切面

AOP 方式日志记录原理分析


项目简介 业务描述

基于Spring Cloud Alibaba解决方案实现文件上传,例如

初始架构设计

本次项目实践,整体上基于前后端分离架构,服务设计上基于spring cloud alibaba解决方案进行实现,例如:

说明,为了降低学习难度,这里只做了初始架构设计,后续会逐步基于这个架构进行演进,例如我们会加上网关工程,认证工程等.

工程创建及初始化 工程结构

参考如下工程结构,进行项目创建,例如:

创建父工程

创建项目父工程用来管理项目依赖.

创建文件服务工程

创建用于处理文件上传业务的工程,例如:

创建客户端服务工程

创建一个客户端工程,在此工程中定义一些静态页面,例如文件上传页面.


父工程初始化

打开父工程的pom.xml文件,添加如下依赖:

 
        
            
                org.springframework.boot
                spring-boot-dependencies
                2.3.2.RELEASE
                pom
                import
            
            
                org.springframework.cloud
                spring-cloud-dependencies
                Hoxton.SR9
                pom
                import
            
            
                com.alibaba.cloud
                spring-cloud-alibaba-dependencies
                2.2.6.RELEASE
                pom
                import
            
        
    
     
        
            org.projectlombok
            lombok
            provided
        
    
文件资源服务实现 添加项目依赖

在sca-resource工程中添加如下依赖:

       
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-config
        
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-sentinel
        
        
        
            org.springframework.boot
            spring-boot-starter-actuator
        
服务初始化配置

在项目的resources目录下创建bootstrap.yml配置文件(假如后续配置信息要写到配置中心配置文件名必须为bootstrap.yml),并添加如下内容:

server:
  port: 8881
spring:
  application:
    name: sca-resource
  servlet:
    multipart:
      max-file-size: 100MB #控制上传文件的大小
      max-request-size: 110MB #请求数据大小
  resources: #定义可以访问到上传资源的路径
    static-locations: file:d:/uploads  #静态资源路径(原先存储到resources/static目录下的资源可以存储到此目录中)
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848
jt:   #这里的配置,后续会在一些相关类中通过@Value注解进行读取
  resource:
      path: d:/uploads  #设计上传文件存储的根目录(后续要写到配置文件)
      host: http://localhost:8881/ #定义上传文件对应的访问服务器
构建项目启动类

在当前工程中,创建项目启动类,例如:

package com.jt;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class FileApplication {
    public static void main(String[] args) {
        SpringApplication.run(FileApplication.class, args);
    }
}

类创建以后,启动当前项目,检测是否可以启动成功,是否有配置错误.

Controller逻辑实现

定义处理上传请求的Controller对象,例如:

package com.jt.resource.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

@Slf4j
@RestController
@RequestMapping("/resource/")
public class ResourceController {
      //当了类的上面添加了@Slf4J就不用自己创建下面的日志对象了
//    private static final Logger log=
//            LoggerFactory.getLogger(ResourceController.class);

     @Value("${jt.resource.path}")
     private String resourcePath;//="d:/uploads/";
     @Value("${jt.resource.host}")
     private String resourceHost;//="http://localhost:8881/";

     @PostMapping("/upload/")
     public String uploadFile(MultipartFile uploadFile) throws IOException {
         //1.创建文件存储目录(按时间创建-yyyy/MM/dd)
         //1.1获取当前时间的一个目录
         String dateDir = DateTimeFormatter.ofPattern("yyyy/MM/dd")
                 .format(LocalDate.now());
         //1.2构建目录文件对象
         File uploadFileDir=new File(resourcePath,dateDir);
         if(!uploadFileDir.exists())uploadFileDir.mkdirs();
         //2.给文件起个名字(尽量不重复)
         //2.1获取原文件后缀
         String originalFilename=uploadFile.getOriginalFilename();
         String ext = originalFilename.substring(
                 originalFilename.lastIndexOf("."));
         //2.2构建新的文件名
         String newFilePrefix=UUID.randomUUID().toString();
         String newFileName=newFilePrefix+ext;
         //3.开始实现文件上传
         //3.1构建新的文件对象,指向实际上传的文件最终地址
         File file=new File(uploadFileDir,newFileName);
         //3.2上传文件(向指定服务位置写文件数据)
         uploadFile.transferTo(file);
         String fileRealPath=resourceHost+dateDir+"/"+newFileName;
         log.debug("fileRealPath {}",fileRealPath);
         //后续可以将上传的文件信息写入到数据库?
         return fileRealPath;
     }
}
跨域配置实现

我们在通过客户端工程,访问文件上传服务时,需要进行跨域配置,在服务端的跨域配置中有多种方案,最常见是在过滤器的层面进行跨域设计,例如:

package com.jt.files.config;


@Configuration
public class CorsFilterConfig {
    @Bean
    public FilterRegistrationBean filterFilterRegistrationBean(){
        //1.对此过滤器进行配置(跨域设置-url,method)
        UrlbasedCorsConfigurationSource configSource=new UrlbasedCorsConfigurationSource();
        CorsConfiguration config=new CorsConfiguration();
         //允许哪种请求头跨域
        config.addAllowedHeader("*");
        //允许哪种方法类型跨域 get post delete put
        config.addAllowedMethod("*");
        // 允许哪些请求源(ip:port)跨域
        config.addAllowedOrigin("*");
        //是否允许携带cookie跨域
        config.setAllowCredentials(true);
        //2.注册过滤器并设置其优先级
        configSource.registerCorsConfiguration("/**", config);
        FilterRegistrationBean fBean= new FilterRegistrationBean(new CorsFilter(configSource));
        fBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return fBean;
    }
}
客户端工程逻辑实现

本次项目我们的客户端工程基于springboot工程进行设计,项目上线时可以将其静态资源直接放到一个静态资源目录中.

添加依赖

在sca-resource-ui工程的pom文件中添加web依赖,例如:


    org.springframework.boot
    spring-boot-starter-web

构建项目启动类
package com.jt;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(ClientApplication .class, args);
    }
}
创建文件上传页面

在工程的resources目录下创建static目录(假如这个目录已经存在则无需创建),然后在此目录创建fileupload.html静态页面,例如:




    
    文件上载演示
    






启动服务访问测试

第一步:启动nacos服务(在这里做服务的注册和配置管理)
第二步:启动sca-resource服务,此服务提供文件上传功能
第三步:启动sca-resource-ui服务,此服务为客户端工程,提供静态资源的访问.所有页面放到此工程中.
第四步:打开浏览器,访问sca-resource-ui工程下的文件上传页面,例如:

 

API网关(Gateway)工程实践 概述

API 网关是外部资源对服务内部资源访问的入口,所以文件上传请求应该首先请求的是网关服务,然后由网关服务转发到具体的资源服务上。

服务调用架构

工程项目结构设计

 

创建网关工程及初始化

第一步:创建sca-resource-gateway工程,例如:

 

第二步:添加项目依赖,例如:

 
        org.springframework.cloud
        spring-cloud-starter-gateway
    
    
        com.alibaba.cloud
        spring-cloud-starter-alibaba-nacos-discovery
    
    
        com.alibaba.cloud
        spring-cloud-starter-alibaba-nacos-config
    


第三步:创建配置文件bootstrap.xml,然后进行初始配置,例如:

server:
  port: 9000
spring:
  application:
    name: sca-resource-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848
        file-extension: yml
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: router01
          uri: lb://sca-resource
          predicates:
            - Path=/sca/resource/upload/**
          filters:
            - StripPrefix=1


第四步:构建项目启动类,并进行服务启动,检测是否正确,例如:

package com.jt;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ResourceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ResourceApplication.class,args);
    }
}
网关跨域配置

当我们基于Ajax技术访问网关时,需要在网关层面进行跨域设计,例如:

package com.jt.config;

import org.springframework.context.annotation.Bean;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlbasedCorsConfigurationSource;

//@Configuration
public class CorsFilterConfig {
    @Bean
    public CorsWebFilter corsWebFilter(){
        //1.构建基于url方式的跨域配置
        UrlbasedCorsConfigurationSource source= new UrlbasedCorsConfigurationSource();
        //2.进行跨域配置
        CorsConfiguration config=new CorsConfiguration();
        //2.1允许所有ip:port进行跨域
        config.addAllowedOrigin("*");
        //2.2允许所有请求头跨域
        config.addAllowedHeader("*");
        //2.3允许所有请求方式跨域:get,post,..
        config.addAllowedMethod("*");
        //2.4允许携带有效cookie进行跨域
        config.setAllowCredentials(true);
        source.registerCorsConfiguration("/**",config);
        return new CorsWebFilter(source);
    }
}

Spring Gateway工程中的跨域设计,除了可以在网关项目中以java代码方式进行跨域过滤器配置,还可以直接在配置文件进行跨域配置,例如:

spring:
  cloud:
    gateway:
      globalcors: #跨域配置
        corsConfigurations:
          '[/**]':
            allowedOrigins: "*"
            allowedHeaders: "*"
            allowedMethods: "*"
            allowCredentials: true


启动工程进行服务访问
首先打开网关(Gateway),资源服务器(Resource),客户端工程服务(UI),然后修改fileupload.html文件中访问资源服务端的url,例如

 let url="http://localhost:9000/sca/resource/upload/";


接下来进行访问测试,例如:

 

启动工程进行服务访问

首先打开网关(Gateway),资源服务器(Resource),客户端工程服务(UI),然后修改fileupload.html文件中访问资源服务端的url,例如

 let url="http://localhost:9000/sca/resource/upload/";

接下来进行访问测试,例如:

网关上对文件上传限流

第一步:在网关pom文件中添加依赖


    com.alibaba.cloud
    spring-cloud-starter-alibaba-sentinel



    com.alibaba.cloud
    spring-cloud-alibaba-sentinel-gateway

 

第二步:在网关配置文件中添加sentinel配置

sentinel:
  transport:
    dashboard: localhost:8180
  eager: true


第三步:在网关项目启动时,配置jvm启动参数,例如:

-Dcsp.sentinel.app.type=1


第四步:先执行一次上传,然后对上传进行限流规则设计

第五步:修改文件上传页面js,对限流结果进行处理,例如:

 function upload(file){
        //定义一个表单(axios中提供的表单对象)
        let form=new FormData();
        //将文件添加到表单中
        form.append("uploadFile",file);
        //异步提交(现在是提交到网关)
        //let url="http://localhost:8881/resource/upload/"
        let url="http://localhost:9000/sca/resource/upload/";
        axios.post(url,form)
            .then(function (response){
                alert("upload ok")
                console.log(response.data);
            })
            .catch(function (e){//失败时执行catch代码块
                //被限流后服务端返回的状态码为429
                if(e.response.status==429){
                    alert("上传太频繁了");
                }
                console.log("error",e);
            })
    }
AOP方式 *** 作日志记录 页面描述

在实现文件上传业务时,添加记录日志的 *** 作.

添加项目依赖

在sca-resource工程中添加AOP依赖,例如:


    org.springframework.boot
    spring-boot-starter-aop

创建切入点注解

我们项目要为目标业务实现功能增强,锦上添花,但系统要指定谁是目标业务,这里我们定义一个注解,后续用此注解描述目标业务。

package com.jt.resource.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredLog {
    String value() default "";
}
定义切入点方法

通过上面定义的注解RequiredLog,对sca-resources工程中的ResourceController文件上传方法进行描述,例如:

@RequiredLog("upload file")
@PostMapping("/upload/")
public String uploadFile(MultipartFile uploadFile) throws IOException {...}

说明:通过@RequiredLog注解描述的方法可以认为锦上添花的“锦”,后续添花的行为可以放在切面的通知方法中。

定义日志 *** 作切面

在AOP编程设计中,我们会通过切面封装切入点(Pointcut)和扩展业务逻辑(Around,…)的定义,例如:

package com.jt.resource.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class LogAspect {
    //定义切入点
    @Pointcut("@annotation(com.jt.resource.annotation.RequiredLog)")
    public void doLog(){}//锦上添花的锦(注解描述的方法)

    //定义扩展业务逻辑
    @Around("doLog()")
    //@Around("@annotation(com.jt.resource.annotation.RequiredLog)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        log.debug("Before {}",System.currentTimeMillis());
        Object result=joinPoint.proceed();//执行执行链(其它切面,目标方法-锦)
        log.debug("After {}",System.currentTimeMillis());
        return result;//目标方法(切入点方法)的执行结果
    }
}
AOP 方式日志记录原理分析

我们在基于AOP方式记录用户 *** 作日志时,其底层工作流程如下:

说明:当我们在项目中定义了AOP切面以后,系统启动时,会对有@Aspect注解描述的类进行加载分析,基于切入点的描述为目标类型对象,创建代理对象,并在代理对象内部创建一个执行链,这个执行链中包含拦截器(封装了切入点信息),通知(Around,…),目标对象等,我们请求目标对象资源时,会直接按执行链的顺序对资源进行调用。
 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5685190.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存