当前位置:   article > 正文

Spring Boot配合七牛云实现文件上传

Spring Boot配合七牛云实现文件上传

1 简介

官网 API 文档:https://developer.qiniu.com/kodo/1239/java
所用技术:

  • Spring Boot 3.2.7
  • lombok
  • qiniu-java-JDK 7.13.0
  • google.code.gson 2.9.1

Gson 库的主要用途是:

  1. 解析 JSON 数据:它允许 Java 应用程序读取 JSON 格式的字符串,并将这些数据转换成 Java 对象,无论是简单的数据类型(如 int、String)还是复杂的对象图。
  2. 生成 JSON 数据:Gson 同样支持将 Java 对象(包括其属性和嵌套对象)转换成 JSON 格式的字符串。这对于需要向 Web 服务发送数据或将数据保存到 JSON 文件中非常有用。

Gson 提供了简洁的 API 来处理 JSON 数据,使得在 Java 应用程序中处理 JSON 变得简单而直观。它支持复杂的对象映射,包括自定义序列化/反序列化、泛型类型处理、多态类型处理等高级功能。

2 七牛云上传代码实现

2.1 导入依赖

<dependency>
  <groupId>com.qiniu</groupId>
  <artifactId>qiniu-java-sdk</artifactId>
  <version>7.13.0</version>
</dependency>
<dependency>
  <groupId>com.google.code.gson</groupId>
  <artifactId>gson</artifactId>
  <version>2.9.1</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2.2 配置 OSS 存储对象信息

在 Spring 的配置文件中配置一下个人信息。.yml文件配置。

server:
  port: 8080

oss:
  qiniu:
    url: http://zp.strivezhang.xyz // 七牛云URL连接(绑定的某个域名)
    accessKey: ********* #秘钥AK
    secretKey: ********* #秘钥SK
    bucketName: haiwai-strivepeng-typora #仓库名(存储空间的名称)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.2 新建 QiNiuYunConfig 配置类

通过@ConfigurationProperties(prefix = "oss.qiniu")将配置文件中的相关配置项自动解析并绑定到类属性上面。

@Data
@ConfigurationProperties(prefix = "oss.qiniu")
@Component
public class QiNiuYunConfig {
    private String url;
    private String accessKey;
    private String secretKey;
    private String bucketName;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.3 构建文件重命名工具类

public class StringUtil {
    public static String getRandomImgName(String fileName){
        // 获取文件后缀
        int index = fileName.lastIndexOf('.');
        String suffix = fileName.substring(index);
        // 校验文件
        if(".jpg".equals(suffix) || ".jpeg".equals(suffix) || ".png".equals(suffix)){
            //改变上传到服务器的文件名  uuid + suffix
            // 生成UUID
            String uuid = UUID.randomUUID().toString().replaceAll("-", "");
            String path = uuid + suffix;
            return path;
        }else{
            throw new IllegalArgumentException();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2.4 编写 Service 实现类

配置时,关于 Zone 的相关配置,其中关于Region对象和机房的关系如下:

机房Region
华东Region.region0(), Region.huadong()
华北Region.region1(), Region.huabei()
华南Region.region2(), Region.huanan()
北美Region.regionNa0(), Region.beimei()
东南亚Region.regionAs0(), Region.xinjiapo()

注意:

  • 若不指定 Region 或 Region.autoRegion() ,则会使用 自动判断 区域,使用相应域名处理。
  • 如果可以明确 区域 的话,最好指定固定区域,这样可以少一步网络请求,少一步出错的可能。
  • 不指定文件上传的 Key 的时候,将以文件的 hash 值作为文件名

基本的流程如下:

  1. 初始化上传配置 init()
  2. 生成上传的 Token
  3. 上传的具体实现
    1. 判断文件是否为图片(后缀判断文件类型)
    2. 通过流的方式将文件转为 BufferedImage 对象,获取长宽属性
    3. 判断文件是否为恶意文件,通过图片的属性(是否具有长宽属性判断)
    4. 调用以流方式上传 API 实现文件上传 通过对文件命名添加 "/img"+ "文件名"的方式可以上传到指定空间的不同文件夹下
    5. 通过自定义的获取文件方法,返回上传的图片信息连接 getPrivateFile(String fileKey)
    6. 结束返回信息
@Service
public class UploadImgService {
    private QiNiuYunConfig qiNiuYunConfig;
    private UploadManager uploadManager;
    private String token;
    private Auth auth;
    private BucketManager bucketManager;

    public UploadImgService(QiNiuYunConfig qiNiuYunConfig){
        this.qiNiuYunConfig = qiNiuYunConfig;
        init();
    }

    private void init() {
        uploadManager = new UploadManager(new Configuration(Zone.zoneNa0()));
        auth = Auth.create(qiNiuYunConfig.getAccessKey(), qiNiuYunConfig.getSecretKey());
        // 生成上传的token
        bucketManager = new BucketManager(auth, new Configuration(Zone.zoneNa0()));
        token = auth.uploadToken(qiNiuYunConfig.getBucketName());
    }

    /**
     * 上传文件
     */
    public String uploadQNImg(MultipartFile file) {
        String resultImage = "失败";
        try {
            // 判断图片后缀,并使用工具类根据上传文件生成唯一图片名称,防止截断字符如“%00”
            String fileName = file.getOriginalFilename();
            String imgName = StringUtil.getRandomImgName(fileName);

            //判断是否为恶意程序
            //通过流的方式把文件转换为BufferedImage对象,获取宽和高,只有图片才具有宽高属性
            BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
            if(bufferedImage == null || bufferedImage.getHeight()==0 || bufferedImage.getWidth()==0){
                return resultImage;
            }

            // 上传图片文件
            Response res = uploadManager.put(file.getInputStream(), "sky/"+imgName, token, null, null);
            if (!res.isOK()) {
                throw new RuntimeException("上传七牛出错:" + res.toString());
            }
            // 直接返回外链地址
            return getPrivateFile(imgName);
        } catch (QiniuException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "失败";
    }

    /**
     * 获取私有空间文件
     *
     */
    public String getPrivateFile(String fileKey) {
        String encodedFileName = null;
        String finalUrl = null;
        try {
            encodedFileName = URLEncoder.encode(fileKey, "utf-8").replace("+", "%20");
            String publicUrl = String.format("%s/%s", this.qiNiuYunConfig.getUrl(), encodedFileName);
            //1小时,可以自定义链接过期时间
            long expireInSeconds = 3600;
            finalUrl = auth.privateDownloadUrl(publicUrl, expireInSeconds);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return finalUrl;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

2.5 编写 controller 类实现接口

@RestController
@RequestMapping("/qiniu")
public class UploadImageController {
    @Autowired
    private UploadImgService uploadImgService;

    @PostMapping("/image")
    public String upLoadImg(@RequestBody MultipartFile file){
        String result = "失败";
        if(!file.isEmpty()){
            String path = uploadImgService.uploadQNImg(file);
            if(path.equals(result)){
                return "上传失败";
            }else{
                System.out.println("七牛云返回的图片链接是:" + path);
                return "上传成功:"+path;
            }
        }
        return "上传失败";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

2.6 运行测试

image.png

3 关于对象存储上传流程

3.1 上传流程

文件上传分为客户端上传(主要是指网页端和移动端等面向终端用户的场景)和服务端上传两种场景,具体可以参考文档业务流程

  • 客户端上传:Java 编写的服务端仅提供上传需要的凭证,网页端和移动端等客户端从服务端获取此凭证后使用客户端相应的 SDK 进行上传操作
  • 服务端上传:文件在服务端或者服务端作为文件的中转站,使用此 Java SDK 上传文件至七牛云端

服务端SDK在上传方面主要提供两种功能

  • 生成客户端上传所需要的上传凭证
  • 服务端上传文件到七牛云端

3.2 上传凭证

客户端(移动端或者Web端)上传文件的时候,需要从客户自己的业务服务器获取上传凭证,而这些上传凭证是通过服务端的SDK来生成的,然后通过客户自己的业务API分发给客户端使用。根据上传的业务需求不同,七牛云Java SDK支持丰富的上传凭证生成方式。以下是一些官方给出的上传方式:

  • 简单上传的凭证:最简单的上传凭证只需要AccessKey,SecretKey和Bucket就可以。
  • 覆盖上传的凭证:覆盖上传除了需要简单上传所需要的信息之外,还需要想进行覆盖的文件名称,这个文件名称同时是客户端上传代码中指定的文件名,两者必须一致。
  • 自定义上传回复的凭证:默认情况下,文件上传到七牛之后,在没有设置returnBody或者回调相关的参数情况下,七牛返回给上传端的回复格式为hash和key。
  • 带回调业务服务器的凭证:上面生成的自定义上传回复的上传凭证适用于上传端(无论是客户端还是服务端)和七牛服务器之间进行直接交互的情况下。在客户端上传的场景之下,有时候客户端需要在文件上传到七牛之后,从业务服务器获取相关的信息,这个时候就要用到七牛的上传回调及相关回调参数的设置。
  • 带数据处理的凭证:存储服务支持在文件上传到七牛之后,立即对其进行多种指令的数据处理,这个只需要在生成的上传凭证中指定相关的处理参数即可。
  • 综合上传凭证:上面生成上传凭证的方法,都是通过设置上传策略
推荐阅读
相关标签