构建SpringCloud 项目—文件上传

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

一、业务描述

基于Spring Cloud Alibaba解决方案实现文件上传,例如:
在这里插入图片描述

二、工程创建及初始化

1、工程结构

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

2、创建父工程:02-sca-files

创建项目父工程用来管理项目依赖
在这里插入图片描述

3、父工程初始化

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

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.jt</groupId>
	<artifactId>02-sca-files</artifactId>
	<version>1.0-SNAPSHOT</version>

	<properties>
		<maven.compiler.source>8</maven.compiler.source>
		<maven.compiler.target>8</maven.compiler.target>
	</properties>

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

</project>

4、创建文件服务工程

创建用于处理文件上传业务的工程,例如:
在这里插入图片描述

5、创建客户端服务工程

创建一个客户端工程,在此工程中定义一些静态页面,例如文件上传页面。
在这里插入图片描述

三、文件资源服务实现

1、创建sca-resource工程,服务端

1.1)添加项目依赖

在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>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>provided</scope>
        </dependency>

1.2)服务初始化配置,bootstrap.yml
server:
  port: 8881
spring:
  application:
    name: sca-resource
  cloud:
    nacos:
      config:
        server-addr: localhost:8848 #配置文件地址
        file-extension: yml
      discovery:
        server-addr: localhost:8848 #从哪里去查找服务
#  resources:  #配置静态资源存储位置(默认resources/static目录),现在写到了配置中心
#    static-locations: file:d:/uploads

#    sentinel:
#      eager: true  #即时通讯,服务启动时就要与sentinel通讯,在sentinel控制台创建服务菜单
#      transport:
#        dashboard: localhost:8180
#        port: 8719

#自定义上传的资源地址和服务器地址(写到了配置中心)
#jt:
#  resource:
#      path: d:/uploads/
#      host: http://localhost:8881/

#logging:
#  level:
#    com.jt: debug

1.3)构建项目启动类
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);
    }
}

1.4)idea 启动nacos

第一步:打开服务编辑配置,例如:
在这里插入图片描述第二步:添加Shell Script,例如:
在这里插入图片描述
第三步:添加nacos相关信息,例如:
在这里插入图片描述
启动成功:
在这里插入图片描述

1.5) 启动sca-resource

在这里插入图片描述

2、创建sca-resource-ui工程,客户端

2.1)添加项目依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2.2)构建项目启动类
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);
    }
}

2.3)创建文件上传页面
2.3.1)创建静态资源目录 static

在这里插入图片描述

2.3.2)定义文件上传页面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>
    <style>
        ul>li{
            list-style-type: none;
        }
    </style>
</head>
<body>
<h1>文件上传案例演示:</h1>
<form id="fileForm" method="post"
                    enctype="multipart/form-data"
                    onsubmit="return doUpload()">
    <div>
        <ul>
            <li><input id="uploadFile" type="file" name="uploadFile"></li>
            <li><button type="submit">上传文件</button></li>
        </ul>
    </div>
</form>
</body>
<script>
    //jquery代码的表单提交事件
    function doUpload(){
        debugger //debug窗口打开以后调试代码
        //获得用户选中的所有图片(获得数组)
        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/";
        //异步提交方式1
        axios.post(url,form)
             .then(function (response){
                 alert("upload ok")
                 console.log(response.data);
             })
             .catch(function (e){//失败时执行catch代码块
                 console.log(e);
             })
        //异步提交方式2
        // axios({
        //     url:"http://localhost:8881/resource/upload/",
        //     method:"post",
        //     data:form
        // }).then(function(response){
        //     alert("upload ok")
        //     console.log(response.data);
        // })
    }
</script>
</html>

2.3.4)启动服务,访问网址:http://localhost:8080/fileupload.html

在这里插入图片描述

3、启动服务访问测试

第一步:启动nacos
第二步:启动sca-resource服务
第三步:启动sca-resource-ui服务
第四步:打开浏览器进行访问测试,例如:
在这里插入图片描述

4、Controller逻辑实现

定义处理上传请求的Controller对象,例如:在这里插入图片描述

package com.jt.resource.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.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 	//lombok自动创建日志对象
//@CrossOrigin //加了过滤器,这个注解就失效了
@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;
     }
}

5、跨域配置实现

访问MVC时,会先经过过过滤器,如果过滤器有配置,controlle层的@CrossOrigin这个注解就失效了,因此,需要配置基于过滤器方式进行跨域配置
我们在通过客户端工程,访问文件上传服务时,需要进行跨域配置,例如:
在这里插入图片描述

package com.jt.resource.config;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

/**
 * 跨域配置(基于过滤器方式进行配置,并且将过滤优先级设置高一些)
 */
@Configuration
public class CorsFilterConfig {
//Spring MVC中注册过滤器使用FilterRegistrationBean对象
    @Bean
    public FilterRegistrationBean<CorsFilter> filterFilterRegistrationBean(){
        //1.对此过滤器进行配置(跨域设置-url,method)
        UrlBasedCorsConfigurationSource configSource=new UrlBasedCorsConfigurationSource();
        CorsConfiguration config=new CorsConfiguration();

        //2.构建url规则
        //2.1允许哪种请求头跨域
        config.addAllowedHeader("*");

        //2.2允许哪种方法类型跨域 get post delete put
        config.addAllowedMethod("*");

        //2.3允许哪些请求源(ip:port)跨域
        config.addAllowedOrigin("*");

        //2.4是否允许携带cookie跨域
        config.setAllowCredentials(true);

        //2.5将这个跨域配置应用到具体的url
        configSource.registerCorsConfiguration("/**", config);

        //3.注册过滤器
        FilterRegistrationBean<CorsFilter> fBean= new FilterRegistrationBean(new CorsFilter(configSource));

        //4.设置过滤器优先级
        fBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return fBean;
    }
}

6、测试,上传是否成功

http://localhost:8080/fileupload.html

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7、上传文件后,文件预览

7.1)修改配置文件:
server:
  port: 8881
spring:
  application:
    name: sca-resource  #应用()名字
  cloud:
    nacos:
      config:
        server-addr: localhost:8848  #配置文件地址
        file-extension: yml
      discovery:
        server-addr: localhost:8848 #从哪里去查找服务
  resources:  #配置静态资源存储位置(默认resources/static目录),现在写到了配置中心
    static-locations: file:d:/uploads //从这和
7.2)测试

在这里插入图片描述
在这里插入图片描述

8、基于后端配置文件bootstrap.yml,动态接收上传路径、预览路径

8.1)修改配置文件
server:
  port: 8881
spring:
  application:
    name: sca-resource
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
        file-extension: yml
      discovery:
        server-addr: localhost:8848
  resources:  #配置静态资源存储位置(默认resources/static目录),现在写到了配置中心
    static-locations: file:d:/uploads

#    sentinel:
#      eager: true  #服务启动时就要与sentinel通讯,在sentinel控制台创建服务菜单
#      transport:
#        dashboard: localhost:8180
#        port: 8719

#自定义上传的资源地址和服务器地址(写到了配置中心)
jt:
  resource:
      path: d:/uploads/
      host: http://localhost:8881/

8.2)修改后端代码
	//读取配置文件中的参数
	//获取上传路径
     @Value("${jt.resource.path}")
     private String resourcePath;//"d:/uploads/";
    
    //获取预览虚拟地址前缀
     @Value("${jt.resource.host}")
     private String resourceHost;//"http://localhost:8881/";

9、基于nacos服务注册中心配置,动态接收上传路径、预览路径

9.1)添加配置

在这里插入图片描述
在这里插入图片描述

9.2)重启nasoc服务

@RefreshScope//这个注解描述类时,当配置中心数据发生变化会对属性进行重新初始化,不需要重启服务

@RefreshScope//这个注解描述类时,当配置中心数据发生变化会对属性进行重新初始化,不需要重启服务
@Slf4j 	//lombok自动创建日志对象
//@CrossOrigin //加了过滤器,这个注解就失效了
@RestController
@RequestMapping("/resource/")

四、API网关(Gateway)工程实践

1、概述

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

2、服务调用架构

在这里插入图片描述
在这里插入图片描述

3、工程项目结构设计

在这里插入图片描述

4、创建网关工程及初始化

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

在这里插入图片描述

第二步:添加项目依赖,例如:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<parent>
		<artifactId>02-sca-files</artifactId>
		<groupId>com.jt</groupId>
		<version>1.0-SNAPSHOT</version>
	</parent>
	<modelVersion>4.0.0</modelVersion>

	<artifactId>02-sca-gateway</artifactId>

	<properties>
		<maven.compiler.source>8</maven.compiler.source>
		<maven.compiler.target>8</maven.compiler.target>
	</properties>


	<dependencies>
		<!--云服务网关依赖-->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-gateway</artifactId>
		</dependency>

		<!--服务的注册和发现(我们要讲服务注册到nacos)-->
		<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>
	</dependencies>

</project>
第三步:创建配置文件bootstrap.xml,然后进行初始配置,例如:
server:
  port: 9000
spring:
  application:
    name: sca-resource-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:  # API Gateway (负责API管理)
      discovery:
        locator:
          enabled: true  #开启通过服务注册中心的serviceId创建路由
      routes:
        - id: route01  #http://localhost:9000/nacos/provider/echo/hello
          #uri: http://localhost:8081/
          uri: lb://sca-resource  #lb表示负载均衡,为服务前缀,不能随意写,底层调用的ribbon实现的
          predicates: ###匹配规则
            - Path=/sca/resource/upload/**
          filters:
            - StripPrefix=1 #转发之前去掉path中第一层路径,例如nacos

logging:
  level:
    org.springframework.cloud.gateway: debug

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

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

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

5、网关跨域配置

当我们基于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: "*"   #允许所有ip:port进行跨域
            allowedHeaders: "*"   #允许所有请求头跨域
            allowedMethods: "*"   #允许所有请求方式跨域:get,post,..
            allowCredentials: true #允许携带有效cookie进行跨域

6、启动工程进行服务访问

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

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

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

http://localhost:8080/fileupload.html

在这里插入图片描述
在这里插入图片描述

问题总结

1、跨域访问问题

在这里插入图片描述

2、Nacos 服务注册问题

在这里插入图片描述

3、文件上传404问题

在这里插入图片描述

4、请求资源405异常

在这里插入图片描述

5、请求资源500异常

在这里插入图片描述

6、BeanCreationException 异常

在这里插入图片描述

7、服务名无效或没有定义

在这里插入图片描述

8、静态资源

1、静态资源源存储位置(默认resources/static目录),存在该目录下,访问时:localhos:端口号/网页名字
2、resources: #配置静态资源存储位置(默认resources/static目录),现在写到了配置中心
static-locations: file:d:/uploads
修改了静态资源,存储在d:/uploads下,访问时:localhos:端口号/uploads/网页

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AimerDaniil

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值