文件上传oss,并查询上传进度(SpringBoot+Redis+Oss+Swagger3)

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
对象存储 OSS,20GB 3个月
云数据库 Tair(兼容Redis),内存型 2GB
简介: 文件上传oss,并查询上传进度(SpringBoot+Redis+Oss+Swagger3)

诉求

      将文件上传到oss,并实时监听上传进度,并将进度进行存储。实现这个功能的由来是有可能上传的文件较大,并不能在调用上传接口得到文件上传成功或者失败的回应

技术选型

SpringBoot 2.4.0:选用SpringBoot可以进行快速开发迭代,社区支持力度较大,搜索问题较为方便

Redis:使用Redis当作文件进度的缓存,并设置过期时间

Oss:选取Aliyun Oss作为文件存储管理器

Swagger3:使用Swagger3可以让后端开发更便捷的在页面上操作接口,方便了接口之间的操作

pom配置

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.0</version>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>8</java.version>
        <java.encoding>UTF-8</java.encoding>
        <slf4j.version>1.7.30</slf4j.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--   集成swagger2代     -->
<!--        <dependency>-->
<!--            <groupId>io.springfox</groupId>-->
<!--            <artifactId>springfox-swagger2</artifactId>-->
<!--            <version>3.0.0</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>io.springfox</groupId>-->
<!--            <artifactId>springfox-swagger-ui</artifactId>-->
<!--            <version>3.0.0</version>-->
<!--        </dependency>-->
 <!--   集成swagger3代     -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.15.0</version>
        </dependency>
        <!-- 引入日志管理相关依赖-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-to-slf4j</artifactId>
            <version>2.14.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>
    </dependencies>
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <target>${java.version}</target>
                        <source>${java.version}</source>
                        <encoding>${java.encoding}</encoding>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.6</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-release-plugin</artifactId>
                    <configuration>
                        <arguments>-Prelease</arguments>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-source-plugin</artifactId>
                    <version>2.1</version>
                    <configuration>
                        <attach>true</attach>
                    </configuration>
                    <executions>
                        <execution>
                            <phase>compile</phase>
                            <goals>
                                <goal>jar</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

项目结构

文件树

.
├── HELP.md
├── mvnw
├── mvnw.cmd
├── pom.xml
├── springboot-test.iml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── example
│   │   │           └── demo
│   │   │               ├── DemoApplication.java
│   │   │               ├── ProgressInfo.java
│   │   │               ├── ServletInitializer.java
│   │   │               ├── component
│   │   │               │   └── OssComponent.java
│   │   │               ├── config
│   │   │               │   ├── CorsFilter.java
│   │   │               │   └── SwaggerConfig.java
│   │   │               ├── controller
│   │   │               │   └── FileController.java
│   │   │               └── service
│   │   │                   └── FileService.java
│   │   └── resources
│   │       ├── application.properties
│   │       ├── application.yaml
│   │       ├── static
│   │       │   └── styles.css
│   │       └── templates
│   │           └── index.html
│   └── test
│       └── java
│           └── com
│               └── example
│                   └── demo
│                       └── DemoApplicationTests.java
└── target
    ├── classes
    │   ├── application.properties
    │   ├── application.yaml
    │   ├── com
    │   │   └── example
    │   │       └── demo
    │   │           ├── DemoApplication.class
    │   │           ├── ProgressInfo.class
    │   │           ├── ServletInitializer.class
    │   │           ├── component
    │   │           │   ├── OssComponent$1.class
    │   │           │   ├── OssComponent$PutObjectProgressListener.class
    │   │           │   └── OssComponent.class
    │   │           ├── config
    │   │           │   ├── CorsFilter.class
    │   │           │   ├── SwaggerConfig$1.class
    │   │           │   └── SwaggerConfig.class
    │   │           ├── controller
    │   │           │   └── FileController.class
    │   │           └── service
    │   │               └── FileService.class
    │   ├── static
    │   │   └── styles.css
    │   └── templates
    │       └── index.html
    ├── generated-sources
    │   └── annotations
    ├── generated-test-sources
    │   └── test-annotations
    └── test-classes
        └── com
            └── example
                └── demo
                    └── DemoApplicationTests.class
37 directories, 34 files
fanlongfeideMacBook-Pro:springboot-test dasouche$ tree
.
├── HELP.md
├── mvnw
├── mvnw.cmd
├── pom.xml
├── springboot-test.iml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── example
│   │   │           └── demo
│   │   │               ├── DemoApplication.java
│   │   │               ├── ServletInitializer.java
│   │   │               ├── component
│   │   │               │   └── OssComponent.java
│   │   │               ├── config
│   │   │               │   ├── CorsFilter.java
│   │   │               │   └── SwaggerConfig.java
│   │   │               ├── controller
│   │   │               │   └── FileController.java
│   │   │               └── service
│   │   │                   └── FileService.java
│   │   └── resources
│   │       ├── application.properties
│   │       ├── application.yaml
│   │       ├── static
│   │       └── templates
│   └── test
│       └── java
│           └── com
│               └── example
│                   └── demo
│                       └── DemoApplicationTests.java
└── target
    ├── classes
    │   ├── application.properties
    │   ├── application.yaml
    │   ├── com
    │   │   └── example
    │   │       └── demo
    │   │           ├── DemoApplication.class
    │   │           ├── ServletInitializer.class
    │   │           ├── component
    │   │           │   ├── OssComponent$1.class
    │   │           │   ├── OssComponent$PutObjectProgressListener.class
    │   │           │   └── OssComponent.class
    │   │           ├── config
    │   │           │   ├── CorsFilter.class
    │   │           │   ├── SwaggerConfig$1.class
    │   │           │   └── SwaggerConfig.class
    │   │           ├── controller
    │   │           │   └── FileController.class
    │   │           └── service
    │   │               └── FileService.class
    │   ├── static
    │   └── templates
    ├── generated-sources
    │   └── annotations
    ├── generated-test-sources
    │   └── test-annotations
    └── test-classes
        └── com
            └── example
                └── demo
                    └── DemoApplicationTests.class
.............................................................................................................................................................................................................................................................................................................................................................................................................................

图示结构

73d8c9be8b2a4960a39693770de0ac9a.png

代码实现

配置相关

配置文件yaml

spring:
  web:
    resources:
      #设置静态文件访问路径,用于直接访问html文件
      static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/templates/
  thymeleaf:
    prefix:  /templates/**
    suffix: .html
    cache: false
  #redis配置
  redis:
    host: xxx
    port: xxx
    password: xxx
    timeout: 30000
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: -1ms
  mvc:
    pathmatch:
      matching-strategy: ANT_PATH_MATCHER
server:
  port: 8080
aliyun:
  OSS_ENDPOINT: http://oss-cn-hangzhou.aliyuncs.com
  ACCESS_ID: xxx
  ACCESS_KEY: xxx
  bucket: xxx

Swagger3配置

package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.ArrayList;
import java.util.List;
/**
 * @author 
 * @date 2023年01月17日 16:00
 */
@Configuration
@EnableOpenApi
public class SwaggerConfig {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.OAS_30) // v2 不同
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.example.demo")) // 设置扫描路径
                .build();
    }
    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addResourceHandlers(ResourceHandlerRegistry registry) {
                registry.addResourceHandler("swagger-ui.html")
                        .addResourceLocations("classpath:/META-INF/resources/");
                registry.addResourceHandler("/webjars/**")
                        .addResourceLocations("classpath:/META-INF/resources/webjars/");
            }
        };
    }
}

跨域问题配置

package com.example.demo.config;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * @author 
 * @date 2023年01月17日 14:46
 */
@Component
public class CorsFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        HttpServletRequest request = (HttpServletRequest) req;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Content-Length, X-Requested-With");
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(req, res);
        }
    }
    @Override
    public void init(FilterConfig filterConfig) {
    }
    @Override
    public void destroy() {
    }
}

oss相关

package com.example.demo.component;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.event.ProgressEvent;
import com.aliyun.oss.event.ProgressEventType;
import com.aliyun.oss.event.ProgressListener;
import com.aliyun.oss.model.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.*;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static com.aliyun.oss.internal.OSSConstants.URL_ENCODING;
/**
 * @author 
 * @date 2023年01月17日 15:11
 */
@Component
@Slf4j
public class OssComponent implements InitializingBean, DisposableBean {
    @Value("${aliyun.OSS_ENDPOINT}")
    private String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
    @Value("${aliyun.ACCESS_ID}")
    private String accessKeyId = "xxx";
    @Value("${aliyun.ACCESS_KEY}")
    private String accessKeySecret = "xxx";
    @Value("${aliyun.bucket}")
    private String bucket = "xxx";
    @Resource
    private RedisTemplate<String, Long> redisTemplate;
    private OSS ossClient;
    //设置缓存失效时间:1天
    private static final TimeUnit TIME_UNIT = TimeUnit.DAYS;
    private static final Integer EXPIRE = 1;
    public String upload(File file, String fileName) throws Exception {
        String requestId = null;
        String etag = null;
        try{
            //用于标识上传文件,用于获取进度时使用
            requestId = UUID.randomUUID().toString();
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, "process-test/" + fileName, file);
            //添加进度条Listener,用于进度条更新
            putObjectRequest.withProgressListener(new PutObjectProgressListener(requestId, redisTemplate));
            //文件
            PutObjectResult putObjectResult = ossClient.putObject(putObjectRequest);
            if(StringUtils.isBlank((etag = putObjectResult.getETag()))){
                throw new RuntimeException("上传失败!");
            }
            return requestId;
        }catch (Exception e){
            log.error("upload error ! requestId : {} etag : {} fileName : {}  " , requestId , etag , fileName , e);
            return null;
        }
    }
    public Integer batchDel(List<String> fileNames) {
        String requestId = null;
        try{
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
            DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucket).withKeys(fileNames).withEncodingType(URL_ENCODING);
            DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(deleteObjectsRequest);
            if(deleteObjectsResult == null){
                return 0;
            }
            requestId = deleteObjectsResult.getRequestId();
            List<String> deletedObjects = deleteObjectsResult.getDeletedObjects();
            if(deletedObjects == null){
                return 0;
            }
            return deletedObjects.size();
        }catch (Exception e){
            log.error("upload error ! requestId : {} fileName : {}  " , requestId , fileNames , e);
            return null;
        }
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    }
    @Override
    public void destroy() throws Exception {
        ossClient.shutdown();
    }
    public static class PutObjectProgressListener implements ProgressListener {
        private String requestId;
        private long bytesWritten = 0;
        private long totalBytes = -1;
        private boolean succeed = false;
        private RedisTemplate redisTemplate;
        public PutObjectProgressListener(String requestId, RedisTemplate redisTemplate) {
            this.requestId = requestId;
            this.redisTemplate = redisTemplate;
            this.redisTemplate.opsForValue().set(requestId + "_total", totalBytes);
            this.redisTemplate.opsForValue().set(requestId + "_uploaded", bytesWritten);
        }
        public PutObjectProgressListener() {
        }
        @Override
        public void progressChanged(ProgressEvent progressEvent) {
            long bytes = progressEvent.getBytes();
            ProgressEventType eventType = progressEvent.getEventType();
            switch (eventType) {
                case TRANSFER_STARTED_EVENT:
                    System.out.println("Start to upload......");
                    break;
                case REQUEST_CONTENT_LENGTH_EVENT:
                    this.totalBytes = bytes;
                    this.redisTemplate.opsForValue().set(requestId + "_total", totalBytes, EXPIRE, TIME_UNIT);
//                    this.totalBytes = bytes;
//                    System.out.println(this.totalBytes + " bytes in total will be uploaded to OSS");
                    break;
                case REQUEST_BYTE_TRANSFER_EVENT:
                    this.bytesWritten += bytes;
                    redisTemplate.opsForValue().set(requestId + "_uploaded", bytesWritten, EXPIRE, TIME_UNIT);
//                    this.bytesWritten += bytes;
//                    if (this.totalBytes != -1) {
//                        int percent = (int)(this.bytesWritten * 100.0 / this.totalBytes);
//                        System.out.println(bytes + " bytes have been written at this time, upload progress: " + percent + "%(" + this.bytesWritten + "/" + this.totalBytes + ")");
//                    } else {
//                        System.out.println(bytes + " bytes have been written at this time, upload ratio: unknown" + "(" + this.bytesWritten + "/...)");
//                    }
                    break;
                case TRANSFER_COMPLETED_EVENT:
                    this.succeed = true;
                    System.out.println("Succeed to upload, " + this.bytesWritten + " bytes have been transferred in total");
                    break;
                case TRANSFER_FAILED_EVENT:
                    System.out.println("Failed to upload, " + this.bytesWritten + " bytes have been transferred");
                    break;
                default:
                    break;
            }
        }
    }
    public static void main(String[] args) {
        String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
        String accessKeyId = "xxx";
        String accessKeySecret = "xxx";
        String bucketName = "xxx";
//
        String key = "process-test/object-get-progress-sample";
        OSS client = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        try {
            File fh = createSampleFile();
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, fh);
            putObjectRequest.<PutObjectRequest>withProgressListener(new PutObjectProgressListener());
            // 带进度条的上传
            PutObjectResult putObjectResult = client.putObject(putObjectRequest);
            String requestId = putObjectResult.getRequestId();
            System.out.println("requestId:" + requestId);
            // 带进度条的下载
//            client.getObject(new GetObjectRequest(bucketName, key).
//                    <GetObjectRequest>withProgressListener(new GetObjectProgressListener()), fh);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * Create a temp file with about 50MB.
     *
     */
    private static File createSampleFile() throws IOException {
        File file = File.createTempFile("oss-java-sdk-", ".txt");
        file.deleteOnExit();
        Writer writer = new OutputStreamWriter(new FileOutputStream(file));
        for (int i = 0; i < 10; i++) {
            writer.write("abcdefghijklmnopqrstuvwxyz\n");
            writer.write("0123456789011234567890\n");
        }
        writer.close();
        return file;
    }
}

Service

package com.example.demo.service;
import com.example.demo.component.OssComponent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.*;
/**
 * @author 
 * @date 2023年01月17日 14:57
 */
@Service
@Slf4j
public class FileService {
    @Resource
    private RedisTemplate<String, Long> redisTemplate;
    @Autowired
    private OssComponent ossComponent;
    /**
     * 获取上传进度
     * @param requestId 文件标识id
     * @return
     */
    public String getUploadFileProcess(String requestId){
        Long totalSize = redisTemplate.opsForValue().get(requestId + "_total");
        Long uploadedSize = redisTemplate.opsForValue().get(requestId + "_uploaded");
        if (null == totalSize || null == uploadedSize){
            return "0%";
        }
        return (int)(uploadedSize * 100.0 / totalSize) + "%";
    }
    /**
     * 模拟文件上传
     * @return
     */
    public String simulateUploadedFile(){
        String requestId = "";
        try {
            File sampleFile = createSampleFile();
            requestId = ossComponent.upload(sampleFile, sampleFile.getName());
        } catch (Exception e) {
            log.error("upload file error!", e);
        }
        return requestId;
    }
    private File createSampleFile() throws IOException {
        File file = File.createTempFile("oss-java-sdk-", ".txt");
        file.deleteOnExit();
        Writer writer = new OutputStreamWriter(new FileOutputStream(file));
        for (int i = 0; i < 10; i++) {
            writer.write("abcdefghijklmnopqrstuvwxyz\n");
            writer.write("0123456789011234567890\n");
        }
        writer.close();
        return file;
    }
}

Controller

package com.example.demo.controller;
import com.example.demo.service.FileService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
 * @author 
 * @date 2023年01月17日 14:34
 */
@RestController()
@RequestMapping("/fileApi")
@Slf4j
@Api(value = "文件接口")
public class FileController {
    @Autowired
    private FileService fileService;
    @ApiOperation("获取上传进度")
    @GetMapping("/uploadProgress")
    public String uploadProgress(String requestId) {
        return fileService.getUploadFileProcess(requestId);
    }
    @ApiOperation("模拟文件上传")
    @GetMapping("/simulateUploadedFile")
    public String simulateUploadedFile() {
        return fileService.simulateUploadedFile();
    }
}

Application

package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import springfox.documentation.oas.annotations.EnableOpenApi;
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
package com.example.demo;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
public class ServletInitializer extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(DemoApplication.class);
    }
}

Swagger接口操作

启动项目无报错后访问:http://localhost:8080/swagger-ui/index.html#/

73d8c9be8b2a4960a39693770de0ac9a.png

可以看到我们的接口在页面上有显示,可以点击对应的接口进行操作

获取上传文件标识号

73d8c9be8b2a4960a39693770de0ac9a.png

73d8c9be8b2a4960a39693770de0ac9a.png

获取文件上传进度

73d8c9be8b2a4960a39693770de0ac9a.png

小结

      文件下载时的进度也可以参考上述代码,进度存储也可以使用其他方式,如ConcurrentHashMap、Mysql等,当然前端也可以实现等。

      Swagger UI页面可以让后端开发更变便捷的操作接口,个人感觉像个快捷版的Postman吧。

Oss官方文档地址: 点我调转

Swagger官方文档地址: 点我调转

Swagger2代3代配置相关疑问可参考文档:点我调转

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
4月前
|
API
支付系统38-----支付宝支付---统一收单线下交易查询 第一步下单------》发起支付请求,登录,确认支付,查单接口开发,swagger接口全部呈现,
支付系统38-----支付宝支付---统一收单线下交易查询 第一步下单------》发起支付请求,登录,确认支付,查单接口开发,swagger接口全部呈现,
|
8天前
|
SQL 前端开发 关系型数据库
SpringBoot使用mysql查询昨天、今天、过去一周、过去半年、过去一年数据
SpringBoot使用mysql查询昨天、今天、过去一周、过去半年、过去一年数据
37 9
|
2月前
|
前端开发 应用服务中间件 API
|
2月前
|
XML JSON Java
springboot文件上传,单文件上传和多文件上传,以及数据遍历和回显
本文介绍了在Spring Boot中如何实现文件上传,包括单文件和多文件上传的实现,文件上传的表单页面创建,接收上传文件的Controller层代码编写,以及上传成功后如何在页面上遍历并显示上传的文件。同时,还涉及了`MultipartFile`类的使用和`@RequestPart`注解,以及在`application.properties`中配置文件上传的相关参数。
springboot文件上传,单文件上传和多文件上传,以及数据遍历和回显
|
1月前
|
自然语言处理 搜索推荐 Java
SpringBoot 搜索引擎 海量数据 Elasticsearch-7 es上手指南 毫秒级查询 包括 版本选型、操作内容、结果截图(一)
SpringBoot 搜索引擎 海量数据 Elasticsearch-7 es上手指南 毫秒级查询 包括 版本选型、操作内容、结果截图
48 0
|
1月前
|
存储 自然语言处理 搜索推荐
SpringBoot 搜索引擎 海量数据 Elasticsearch-7 es上手指南 毫秒级查询 包括 版本选型、操作内容、结果截图(二)
SpringBoot 搜索引擎 海量数据 Elasticsearch-7 es上手指南 毫秒级查询 包括 版本选型、操作内容、结果截图(二)
33 0
|
4月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的武汉市公交路线查询系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的武汉市公交路线查询系统的详细设计和实现(源码+lw+部署文档+讲解等)
110 7
|
4月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的城市公交在线查询系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的城市公交在线查询系统的详细设计和实现(源码+lw+部署文档+讲解等)
|
4月前
|
Java 关系型数据库 API
使用Spring Boot和PostgreSQL构建高级查询
使用Spring Boot和PostgreSQL构建高级查询
|
4月前
|
Java
软件开发常用之SpringBoot文件下载接口编写(下),Vue+SpringBoot文件上传下载预览,服务器默认上传是1M,可以调节,调节文件上传大小写法,图片预览,如何预览后下次还能看到,预览写法
软件开发常用之SpringBoot文件下载接口编写(下),Vue+SpringBoot文件上传下载预览,服务器默认上传是1M,可以调节,调节文件上传大小写法,图片预览,如何预览后下次还能看到,预览写法