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>

2.2 配置 OSS 存储对象信息

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

server:
  port: 8080

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

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

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();
        }
    }
}

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

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 "上传失败";
    }
}

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。
  • 带回调业务服务器的凭证:上面生成的自定义上传回复的上传凭证适用于上传端(无论是客户端还是服务端)和七牛服务器之间进行直接交互的情况下。在客户端上传的场景之下,有时候客户端需要在文件上传到七牛之后,从业务服务器获取相关的信息,这个时候就要用到七牛的上传回调及相关回调参数的设置。
  • 带数据处理的凭证:存储服务支持在文件上传到七牛之后,立即对其进行多种指令的数据处理,这个只需要在生成的上传凭证中指定相关的处理参数即可。
  • 综合上传凭证:上面生成上传凭证的方法,都是通过设置上传策略🔗相关的参数来支持的,这些参数可以通过不同的组合方式来满足不同的业务需求,可以灵活地组织你所需要的上传凭证。
  • 上传凭证额外参数:默认会拒绝不在预定义之中的参数。即:String upToken = auth.uploadToken(bucket, key, expireSeconds, putPolicy, true);若允许添加额外参数,可以将参数设置为false。String upToken = auth.uploadToken(bucket, key, expireSeconds, putPolicy, false);

3.3 解析上传结果

解析自定义回复内容
有些情况下,七牛返回给上传端的内容不是默认的hash和key形式,这种情况下,可能出现在自定义returnBody或者自定义了callbackBody的情况下,前者一般是服务端直传的场景,而后者则是接受上传回调的场景,这两种场景之下,都涉及到需要将自定义的回复内容解析为Java的类对象,一般建议在交互过程中,都采用JSON的方式,这样处理起来方法比较一致,而且JSON的方法最通用。
遇到诸如自定义returnBody的情况:

putPolicy.put("returnBody", "{\"key\":\"$(key)\",\"hash\":\"$(etag)\",\"bucket\":\"$(bucket)\",\"fsize\":$(fsize)}");

或者是自定义了callbackBody的情况:

putPolicy.put("callbackBody", "{\"key\":\"$(key)\",\"hash\":\"$(etag)\",\"bucket\":\"$(bucket)\",\"fsize\":$(fsize)}");

我们只需要自己定义了对应的类,例如:

class MyPutRet {
    public String key; // 文件保存的 key
    public String hash; // 文件保存的 Etag
    public String bucket; // 文件保存的 bucket
    public long fsize; // 文件的大小,单位:B
}

然后在获取到上传回复的Response对象时,使用如下的方法就可以转为自定义回复的类对象了:

MyPutRet myPutRet=response.jsonToObject(MyPutRet.class);

3.4 文件下载(获取)
文件下载分为公开空间的文件下载和私有空间的文件下载。

3.4.1 公开空间

1) 手动拼接方式

对于公开空间,其访问的链接主要是将空间绑定的域名拼接上空间里面的文件名即可访问,标准情况下需要在拼接链接之前,将文件名进行urlencode以兼容不同的字符。如果有其他访问处理需求,在文件名之后继续拼接即可。

String fileName = "a/b/qiniu.jpg";
// domainOfBucket 中的域名为用户 bucket 绑定的下载域名,下面域名仅为示例,不可使用
String domainOfBucket = "http://mock.qiniu.com";
String encodedFileName = URLEncoder.encode(fileName, "utf-8").replace("+", "%20");
String finalUrl = String.format("%s/%s", domainOfBucket, encodedFileName);
System.out.println(finalUrl);
2)sdk 自动生成方式

sdk 封装了下载 URL 的生成,只需要输入下载所需参数即可。已支持多媒体处理命令等。

// domain   用户 bucket 绑定的下载域名 eg: mock.qiniu.com【必须】
// useHttps 是否使用 https【必须】
// key      下载资源在七牛云存储的 key【必须】
DownloadUrl url = new DownloadUrl(domain, useHttps, key);
url.setAttname(attname) // 配置 attname
   .setFop(fop) // 配置 fop
   .setStyle(style, styleSeparator, styleParam) // 配置 style
String urlString = url.buildURL();
System.out.println(urlString);

3.4.2 私有空间

1) 手动拼接方式

对于私有空间,首先需要按照公开空间的文件访问方式构建对应的公开空间访问链接,然后再对这个链接进行私有授权签名。

String fileName = "公司/存储/qiniu.jpg";
String domainOfBucket = "http://devtools.qiniu.com";
String encodedFileName = URLEncoder.encode(fileName, "utf-8").replace("+", "%20");
String publicUrl = String.format("%s/%s", domainOfBucket, encodedFileName);
String accessKey = "your access key";
String secretKey = "your secret key";
Auth auth = Auth.create(accessKey, secretKey);
long expireInSeconds = 3600;//1小时,可以自定义链接过期时间
String finalUrl = auth.privateDownloadUrl(publicUrl, expireInSeconds);
System.out.println(finalUrl);
2)sdk 自动生成方式

sdk 封装了下载 URL 的生成,只需要输入下载所需参数即可。已支持多媒体处理命令,过期时间 e 和 签名 token 等。

// domain   下载 domain, eg: qiniu.com【必须】
// useHttps 是否使用 https【必须】
// key      下载资源在七牛云存储的 key【必须】
DownloadUrl url = new DownloadUrl(domain, useHttps, key);
url.setAttname(attname) // 配置 attname
   .setFop(fop) // 配置 fop
   .setStyle(style, styleSeparator, styleParam) // 配置 style

// 带有效期
long expireInSeconds = 3600;//1小时,可以自定义链接过期时间
long deadline = System.currentTimeMillis()/1000 + expireInSeconds;
Auth auth = Auth.create("your access key", "your secret key");
String urlString = url.buildURL(auth, deadline);
System.out.println(urlString);

相关推荐

  1. springboot实现文件下载

    2024-07-18 05:08:02       30 阅读
  2. SpringBoot实战】基于阿里实现文件

    2024-07-18 05:08:02       60 阅读
  3. Spring Boot + Vue3 实现大视频

    2024-07-18 05:08:02       45 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-18 05:08:02       70 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-18 05:08:02       74 阅读
  3. 在Django里面运行非项目文件

    2024-07-18 05:08:02       62 阅读
  4. Python语言-面向对象

    2024-07-18 05:08:02       72 阅读

热门阅读

  1. 入门C语言只需一个星期(星期二)

    2024-07-18 05:08:02       23 阅读
  2. 国产大模型体验:DeepSeek、Kimi与智谱清言

    2024-07-18 05:08:02       21 阅读
  3. 雅思词汇及发音积累 2024.7.17

    2024-07-18 05:08:02       29 阅读
  4. PHP开发工具:打造高效的编码体验

    2024-07-18 05:08:02       22 阅读
  5. 理解 App Store 审核规则 3.2(f):预防被拒绝的方法

    2024-07-18 05:08:02       24 阅读
  6. VINS介绍

    2024-07-18 05:08:02       27 阅读
  7. CST高频仿真的网格技术

    2024-07-18 05:08:02       35 阅读
  8. 泰勒展开的推导及应用

    2024-07-18 05:08:02       22 阅读
  9. kotlin get set

    2024-07-18 05:08:02       22 阅读