微服务架构08-微服务文件上传

目录

项目简介

业务描述

初始架构设计

工程创建及初始化

工程结构

创建父工程

创建文件服务工程

创建客户端服务工程

父工程初始化

文件资源服务实现

添加项目依赖

服务初始化配置

构建项目启动类

Controller逻辑实现

跨域配置实现

客户端工程逻辑实现

添加依赖

构建项目启动类

创建文件上传页面

启动服务访问测试

API网关(Gateway)工程实践

概述

服务调用架构

工程项目结构设计

创建网关工程及初始化

网关跨域配置

启动工程进行服务访问


项目简介

业务描述

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

初始架构设计

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

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

工程创建及初始化

工程结构

参考如下工程结构,进行项目创建,例如:
在这里插入图片描述

 

创建父工程

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

在这里插入图片描述

 

 

创建文件服务工程

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

 

 

创建客户端服务工程

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

 

父工程初始化

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

 

 <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.3.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR9</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.6.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
     <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

文件资源服务实现

添加项目依赖

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

       <!--Spring Boot Web (服务-内置tomcat)-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--Nacos Discovery (服务注册发现)-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--Nacos Config (配置中心)-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!--Sentinel (流量防卫兵-限流和熔断)-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!--Spring Boot 监控-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

服务初始化配置

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

server:
  port: 8881
spring:
  application:
    name: sca-resource
  servlet:
    multipart:
      max-file-size: 1000MB #控制上传文件的大小
      max-request-size: 1000MB #请求数据大小
  resources: #localhost:8881/app.png
    static-locations: file:D:/pengke/ready/uploads #静态资源的存储目录,默认是resource/static
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848
        file-extension: yml
jt:
  resource:
    path: D:/pengke/ready/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.cy.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.CrossOrigin;
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 sun.util.calendar.LocalGregorianCalendar;

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

@RestController
@RequestMapping("/resource/")
@Slf4j
@RefreshScope//假如属性的值来自配置中心,配置中心内容变化,属性的值也要变化,就需要添加上此注解
//@CrossOrigin //用于在controller层面处理跨域
public class ResourceController {

    @Value("${jt.resource.path}")
    private String resourcePath;
    @Value("${jt.resource.host}")
    private String resourceHost;



    /**
     * 通过此方法处理文件上传的请求
     * @param uploadFile 接收要上传的文件数据(参数名一定要与客户端提交
     * @return 文件上传以后在服务器的实际存储路径,可以基于http协议访问到资源
     * */

    @PostMapping("/upload/")//文件上传的请求方式必须是post
    public String uploadFile(MultipartFile uploadFile) throws IOException {

        /*1.创建文件的存储目录(按年月日的结构进行存储)*/
        //1.1 获取当前日期对应的字符串
        //1.1.1 方式1
        //SimpleDateFormat sdf = new SimpleDateFormat("/yyyy/MM/dd");
        //String dateStr = sdf.format(new Date());

        //1.1.2 方式二(基于jdk8中提供的日期API)
        String dateStr =
                DateTimeFormatter.ofPattern("/yyyy/MM/dd/").format(LocalDate.now());
        log.debug("dateStr : {}",dateStr);

        //1.2创建文件目录
        File uploadDir = new File(resourcePath,dateStr);
        if(!uploadDir.exists())uploadDir.mkdirs();

        /*2.给文件一个新的名字(文件前缀随机产生,文件后缀不能变)*/
        //2.1获取原始文件名
        String originalFilename = uploadFile.getOriginalFilename();
        //2.2构建文件前缀
        String filePrefix = UUID.randomUUID().toString();
        //2.3获取文件名的后缀
        String fileSuffix = originalFilename.substring(originalFilename.lastIndexOf("."));
        //2.4构建新的文件名
        String newFilename = filePrefix+fileSuffix;
        log.debug("newFilename {}",newFilename);

        /*3.上传文件到指定目录*/
        uploadFile.transferTo(new File(uploadDir,newFilename));


        /*4.通过http协议可以访问到的资源路径*/
        String accessAddress = resourceHost+dateStr+"/"+newFilename;
        log.debug("accessAddress: {} ",accessAddress);
        return  accessAddress;

跨域配置实现

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

package com.cy.config;

import org.apache.catalina.filters.CorsFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.web.cors.CorsConfiguration;

import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

//方案2:在过滤器层面实现跨域配置
//@Configuration
public class CorsFilterConfig {
    /**
     * 定义跨域过滤器的配置,并且将其注册到spring容器中*/

    @Bean
    public FilterRegistrationBean<CorsFilter> 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<CorsFilter> fBean= new FilterRegistrationBean(new org.springframework.web.filter.CorsFilter (configSource));
            fBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
            return fBean;

客户端工程逻辑实现

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

添加依赖

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

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

构建项目启动类

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静态页面,例如:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件上载演示</title>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<form id="fileForm" method="post" enctype="multipart/form-data" onsubmit="return doUpload()">
    <div>
        <label>上传文件
            <input id="uploadFile" type="file" name="uploadFile">
        </label>
    </div>
    <button type="submit">上传文件</button>
</form>
</body>
<script>
    //jquery代码的表单提交事件
    function doUpload(){
        //获得用户选中的所有图片(获得数组)
        let files=document.getElementById("uploadFile").files;
        if(files.length>0){
            //获得用户选中的唯一图片(从数组中取出)
            let file=files[0];
            //开始上传这个图片
            //由于上传代码比较多,不想和这里其它代码干扰,所以定义一个方法调用
            upload(file);
        }
        //阻止表单提交效果
        return false;
    };
    // 将file上传到服务器的方法
    function upload(file){
        //定义一个表单
        let form=new FormData();
        //将文件添加到表单中
        form.append("uploadFile",file);
        //异步提交
        let url="http://localhost:8881/resource/upload/";
        axios.post(url,form)
             .then(function (response){
                 alert("upload ok")
                 console.log(response.data);
             })
             .catch(function (e){//失败时执行catch代码块
                 console.log(e);
         })
    }
</script>
</html>

启动服务访问测试

第一步:启动nacos服务(在这里做服务的注册和配置管理)
第二步:启动sca-resource服务,此服务提供文件上传功能

第三步:启动sca-resource-ui服务,此服务为客户端工程,提供静态资源的访问.所有页面放到此工程中.
第四步:打开浏览器,访问sca-resource-ui工程下的文件上传页面,例如:

 

API网关(Gateway)工程实践

概述

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

服务调用架构

 

工程项目结构设计

 

创建网关工程及初始化

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

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

 <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>

第三步:创建配置文件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
#      globalcors: #跨域配置
#        corsConfigurations:
#          '[/**]':
#            allowedOrigins: "*"
#            allowedHeaders: "*"
#            allowedMethods: "*"
#            allowCredentials: true

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

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技术访问网关时,需要在网关层面进行跨域设计,例如:

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

//跨域分析
//1.@CrossOrigin 可以从controller层面解决单个controller跨域问题
//2.@CorsFilter 这个springMVC 中给出过滤器层面的跨域,可以解决多个controller的跨域问题

//3.网关中的CorsWebFilter 这个是spring webflux中的过滤器,可以解决网关层面
// 解决多个服务的跨域问题,这就不需要每个服务都写一遍跨域了

//@Configuration

public class CorsFilterConfig {
    /**
     * 过滤器的层面配置跨域
     * @return 为网关层面的跨域过滤器
     * */
    @Bean
    public CorsWebFilter corsWebFilter(){

        UrlBasedCorsConfigurationSource configSource =
                new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        config.setAllowCredentials(true);
        configSource.registerCorsConfiguration("/**",config);


        return new CorsWebFilter(configSource);
    }
}

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

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

启动工程进行服务访问

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

 let url="http://localhost:9999/nacos/resource/upload/";

接下来进行访问测试,例如:
在这里插入图片描述

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值