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

news2024/10/6 22:30:07

文章目录

  • 诉求
  • 技术选型
  • pom配置
  • 项目结构
    • 文件树
  • 图示结构
  • 代码实现
    • 配置相关
      • 配置文件yaml
      • Swagger3配置
      • 跨域问题配置
      • oss相关
    • Service
    • Controller
    • Application
  • Swagger接口操作
    • 获取上传文件标识号
    • 获取文件上传进度
  • 小结

诉求

       将文件上传到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

图示结构

在这里插入图片描述

代码实现

配置相关

配置文件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#/

在这里插入图片描述

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

获取上传文件标识号

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

获取文件上传进度

在这里插入图片描述

小结

       文件下载时的进度也可以参考上述代码,进度存储也可以使用其他方式,如ConcurrentHashMap、Mysql等,当然前端也可以实现等。
       Swagger UI页面可以让后端开发更变便捷的操作接口,个人感觉像个快捷版的Postman吧。

Oss官方文档地址: 点我调转
Swagger官方文档地址: 点我调转
Swagger2代3代配置相关疑问可参考文档:点我调转

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/168401.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【javaSE】中基本类型和引用类型对象的比较及PriorityQueue中的比较方法

写博客是为了提升自己&#xff0c;也是为了展现自己的学习成果&#xff0c;坚持!坚持!坚持&#xff01;未来是什么样的&#xff0c;闯一闯就知道啦。喜欢就留个关注吧&#xff01;&#xff01;! 目录 一、java对象的比较 1.1java中基本类型的比较 1.2引用对象的比较 1.3引用…

使用云端的GPU进行yolov5的训练

前言本文介绍了使用云端GPU进行yolov5训练环境配置的过程一、创建实例这里使用的是恒源云的GPU服务器&#xff0c;官方网址为恒源云_GPUSHARE-恒源智享云他的用户文档为Tmux - 恒源云用户文档一般的问题在用户文档中都可以找到解决办法。注册并登录后的界面如下图所示。点击云市…

c++11 标准模板(STL)(std::forward_list)(十)

定义于头文件 <forward_list> template< class T, class Allocator std::allocator<T> > class forward_list;(1)(C11 起)namespace pmr { template <class T> using forward_list std::forward_list<T, std::pmr::polymorphic_…

UPerNet:Unified Perceptual Parsing for Scene Understanding论文解读

Unified Perceptual Parsing for Scene Understanding 论文&#xff1a;[1807.10221] Unified Perceptual Parsing for Scene Understanding (arxiv.org) 代码&#xff1a;CSAILVision/unifiedparsing: Codebase and pretrained models for ECCV’18 Unified Perceptual Parsi…

第二章.线性回归以及非线性回归—岭回归

第二章.线性回归以及非线性回归 2.12 岭回归&#xff08;Ridge Regression&#xff09; 1.前期导入&#xff1a; 1).标准方程法[w(XTX)-1XTy]存在的缺陷&#xff1a; 如果数据的特征比样本点还多&#xff0c;数据特征n&#xff0c;样本个数m&#xff0c;如如果n>m&#xf…

5种气血不足的面相

我们常用“气色好”形容人良好的健康状态&#xff0c;反之&#xff0c;气血不足就是不健康的表现。想知道自己是否气血不足&#xff0c;可以从以下几种表现中判断。眼白黄&#xff1a;所谓人老珠黄&#xff0c;就是指眼白的颜色变得浑浊、发黄、有血丝&#xff0c;很可能气血不…

网络编程基础(1)

1 OSI七层模型&#xff08;理论&#xff09; 七层模型&#xff0c;亦称OSI&#xff08;Open System Interconnection&#xff09;。参考模型是国际标准化组织&#xff08;ISO&#xff09;制定的一个用于计算机或通信系统间互联的标准体系&#xff0c;一般称为OSI参考模型或七层…

cycle_gan使用教程

junyanz/pytorch-CycleGAN-and-pix2pix: Image-to-Image Translation in PyTorch (github.com) 如果是用cycle_gan 数据集 /数据集文件夹名&#xff0c;下面四个子文件名 testA testB trainA trainB trainA是A风格图片&#xff0c;trainB是B风格图片。 训练参数 test…

CCF BDCI | 算能赛题决赛选手说明论文-04

基于TPU平台实现人群密度估计 队名&#xff1a;innovation 陈照照 数据科学与大数据技术20级 台州学院 中国-瑞安 479253198qq.com董昊数据科学与大数据技术20级 台州学院 中国-杭州 donghaowifi163.com陈晓聪数据科学与大数据技术20级 台州学院 中国-宁波 2637491…

Golang -- openwechat微信发送消息、自动回复

开篇 马上就要到农历新年了&#xff0c;不妨写一段代码准时为好友们送上祝福。 该 Demo 使用开源项目 openwechat &#xff0c;实现获取好友列表、为好友发送消息、图片或文件&#xff0c;接收来自好友或群组的消息并设置自动回复等功能。 openwechat Github地址 openwechat 文…

管道(匿名,有名)

文章目录Linux 进程间通信的方式管道匿名管道有名管道Linux 进程间通信的方式 管道 管道特点 管道其实是一个在内核内存中维护的缓冲器&#xff0c;这个缓冲器的存储能力是有限的&#xff0c;不同的操作系统大小不一定相同管道拥有文件的特质&#xff1a;读操作、写操作 匿名管…

线扫相机DALSA-变行高拍照

CamExpert在线阵模式中默认的Buffer设置是Fixed Length。在这种设置下&#xff0c;在一帧采集结束前所接收到的新的帧触发信号都会被忽略。在有的应用中&#xff0c;需要新一帧的外触发信号能够中断当前帧的采集&#xff0c;开始新的一帧。这需要将Buffer设为Variable Length。…

【云原生】k8s之HPA,命名空间资源限制

内容预知 1.HPA的相关知识 2.HPA的部署运用 2.1 进行HPA的部署设置 2.2 HPA伸缩的测试演示 &#xff08;1&#xff09;创建一个用于测试的pod资源 (2)创建HPA控制器&#xff0c;进行资源的限制&#xff0c;伸缩管理 &#xff08;3&#xff09;进入其中一个pod容器仲&#xf…

Redhat OpenStack使用命令行发放云主机

OpenStack中各大组件的作用Glance&#xff1a;负责管理镜像&#xff08;镜像的上传、删除、下载&#xff09;Swift&#xff1a;提供镜像存储的空间Nova&#xff1a;负责配额的修改、启动云主机&#xff08;实例&#xff09;、创建密钥对、绑定弹性IP等Keystone&#xff1a;提供…

jQuery(二):属性、元素、尺寸位置操作、事件

jQuery属性操作内容文本值元素操作尺寸、位置操作事件注册事件处理事件对象拷贝对象属性操作 1.获取固有属性语法 prop(‘‘属性’’) 固有属性就是html自带的&#xff0c;例如a元素里面的 href &#xff0c;input 元素里面的 type。 2.设置固有属性语法 prop(‘‘属性’’, …

Python NumPy 搜索 数组

前言NumPy&#xff08;Numerical Python的缩写&#xff09;是一个开源的Python科学计算库。使用NumPy&#xff0c;就可以很自然地使用数组和矩阵。NumPy包含很多实用的数学函数&#xff0c;涵盖线性代数运算、傅里叶变换和随机数生成等功能。本文主要介绍Python NumPy 搜索 数组…

Linux小黑板(8)管道

"让我们,笑吧"一、什么是通信?管道是属于进程间通信的一个实现方式。再讲管道之前呢&#xff0c;我们先来说说什么叫做进程间通信。我们日常生活中&#xff0c;给自己的家人、朋友给一个call&#xff0c;或者弹一条微信、QQ等等&#xff0c;从而让人家能够知道我们想…

Databend 开源周报第 76 期

英文版移步&#xff1a;https://databend.rs/blog/2023-01-11-databend-weekly Databend 是一款强大的云数仓。专为弹性和高效设计。自由且开源。即刻体验云服务&#xff1a;https://app.databend.com 。 What’s New 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的…

vue 中由浅拷贝引发问题的一些场景

在工作的过程中踩了很多的由浅拷贝导致的坑&#xff0c;今天总结在这里&#xff0c;希望对大家有所帮助 1. 组件中直接抛出一个引用类型变量 &#x1f330;举个例子 &#xff08;ps: 以下代码为伪代码&#xff0c;主要展示逻辑用&#xff09; 子组件&#xff08;uploadImg&a…

线 程 同 步、线程的死锁问题

线程同步&#xff1a; 模拟售票程序出现问题&#xff1a;当多个线程同时访问共享数据时&#xff0c;产生无序、重复、超额售票等多线程安全问题 解决&#xff1a;将多个线程需要访问的共享数据&#xff0c;包装起来视为一个整体&#xff0c;确保一次只有一个线程执行流访问共享…