在做项目的过程中需要存储图片,所以选择将图片存储到七牛云上面,并将存储路径返回存储到数据库里。在网上看了很多篇文章,参考了一篇文章,结合自己的功能需求跑了一遍,记录下整个完成的过程。


目录

一、引入七牛云pom依赖:

二、application.yml:

三、在config里绑定配置文件:

四、在util里编写生成唯一图片名称的工具类:

五、编写上传文件接口UploadImageService:

六、 编写接口实现类UploadImageServiceImpl:

六、UploadController:

七、在postman测试一下:


一、引入七牛云pom依赖:

一开始我是直接在七牛云官方文档上直接复制下来的,但是在有些地方引用的时候说不能使用过时的类或方法,所以我就直接固定版本了。

springboot 上传图片到static目录_后端

<!--        七牛云依赖-->
<!--        <dependency>-->
<!--            <groupId>com.qiniu</groupId>-->
<!--            <artifactId>qiniu-java-sdk</artifactId>-->
<!--            <version>[7.7.0, 7.7.99]</version>-->
<!--        </dependency>-->
        <dependency>
            <groupId>com.qiniu</groupId>
            <artifactId>qiniu-java-sdk</artifactId>
            <version>7.2.18</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>3.14.2</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.qiniu</groupId>
            <artifactId>happy-dns-java</artifactId>
            <version>0.1.6</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

二、application.yml:

这些都可以在七牛云找到

springboot 上传图片到static目录_spring_02

oss:
  qiniu:
    url: http://七牛云的外链域名
    accessKey: 密钥AK
    secretKey: 密钥SK
    bucketName: 仓库名

三、在config里绑定配置文件:

package com.walnut.barter.config;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

/**七牛云配置类  绑定配置文件
 * @ClassName QiNiuYunConfig
 * @Description TODO
 * @Author Flechazo_lalala
 * Date 2021-11-17 17:02
 * @Version 1.0
 **/


@Data
@Configuration
public class QiNiuYunConfig {

    @Value("${oss.qiniu.url}")
    private String url;

    @Value("${oss.qiniu.accessKey}")
    private String AccessKey;

    @Value("${oss.qiniu.secretKey}")
    private String SecretKey;

    @Value("${oss.qiniu.bucketName}")
    private String BucketName;

}

四、在util里编写生成唯一图片名称的工具类:

package com.walnut.barter.common.util;

import java.util.UUID;

/**七牛云生成唯一图片名称的工具类
 * @ClassName StringUtil
 * @Description TODO
 * @Author Flechazo_lalala
 * Date 2021-11-17 17:07
 * @Version 1.0
 **/
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();
        }
    }
}

五、编写上传文件接口UploadImageService:

package com.walnut.barter.service;

import org.springframework.web.multipart.MultipartFile;

/**
 * @ClassName UploadImageService
 * @Description TODO
 * @Author Flechazo_lalala
 * Date 2021-11-17 17:09
 * @Version 1.0
 **/
public interface UploadImageService {

    /**
     * 上传文件
     * @param file
     * @return
     */
    String uploadQNImg(MultipartFile file);


}

六、 编写接口实现类UploadImageServiceImpl:

我是华南地区的所以是zone2,如果是其他地区的需要修改。

华东:zone0    华北:zone1   华南:zone2   北美:na0   东南亚:as0

package com.walnut.barter.service.impl;

import com.google.gson.Gson;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import com.walnut.barter.common.util.StringUtil;
import com.walnut.barter.config.QiNiuYunConfig;
import com.walnut.barter.service.UploadImageService;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

/**
 * @ClassName UploadImageServiceImpl
 * @Description TODO
 * @Author Flechazo_lalala
 * Date 2021-11-17 17:11
 * @Version 1.0
 **/


@Service
public class UploadImageServiceImpl implements UploadImageService {

    private QiNiuYunConfig qiNiuYunConfig;

    /**七牛文件上传管理器*/
    private UploadManager uploadManager;
    /**上传的token*/
    private String token;
    /**七牛认证管理*/
    private Auth auth;

    private BucketManager bucketManager;

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

    private void init() {
        // 我是华南地区的所以是zone2,如果是其他地区的需要修改
        uploadManager = new UploadManager(new Configuration(Zone.zone2()));
        auth = Auth.create(qiNiuYunConfig.getAccessKey(), qiNiuYunConfig.getSecretKey());
        // 根据命名空间生成的上传token
        bucketManager = new BucketManager(auth, new Configuration(Zone.zone2()));
        token = auth.uploadToken(qiNiuYunConfig.getBucketName());
    }


    /**
     * 上传文件
     * @param file
     * @return
     */
    @Override
    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(), imgName, token, null, null);
            if (!res.isOK()) {
                throw new RuntimeException("上传七牛出错:" + res.toString());
            }
            // 解析上传成功的结果
            DefaultPutRet putRet = new Gson().fromJson(res.bodyString(), DefaultPutRet.class);

            // 直接返回外链地址
            return getPrivateFile(imgName);
        } catch (QiniuException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "失败";
    }

    /**
     * 获取私有空间文件
     *
     * @param fileKey
     * @return
     */
    @Override
    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;
    }


    /**
     * 根据空间名、文件名删除文件
     * @param bucketName
     * @param fileKey
     * @return
     */
    @Override
    public boolean removeFile(String bucketName, String fileKey) {
        try {
            bucketManager.delete(bucketName, fileKey);
        } catch (QiniuException e) {
            e.printStackTrace();
        }
        return true;
    }

}

六、UploadController:

最后是控制层啦UploadController

这里的ResultInfo是封装好的统一接口类,大家可以自己修改,无伤大雅。

package com.walnut.barter.controller;


import com.walnut.barter.common.lang.ResultInfo;
import com.walnut.barter.common.util.ResultInfoUtil;
import com.walnut.barter.domain.vo.QiniuVo;
import com.walnut.barter.service.UploadImageService;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;

/**
 * @ClassName UploadController
 * @Description TODO
 * @Author Flechazo_lalala
 * Date 2021-11-17 17:20
 * @Version 1.0
 **/
@Slf4j
@RestController
@RequestMapping("/qiniu")
public class UploadController {

    @Autowired
    private UploadImageService uploadImageService;

    @ApiOperation("上传图片")
    @PostMapping("/image")
    public ResultInfo upLoadImage(@RequestParam("file") MultipartFile file){
        String result = "失败";
        QiniuVo qiniuVo = new QiniuVo();
        if(!file.isEmpty()){
            String path = uploadImageService.uploadQNImg(file);
            if(path.equals(result)){
                return ResultInfoUtil.buildError("上传失败");
            }else{
                System.out.println("七牛云返回的图片链接是:" + path);
                qiniuVo.setPath(path);
                return ResultInfoUtil.buildSuccess(qiniuVo);
            }
        }
        return ResultInfoUtil.buildError("上传失败");
    }

}

七、在postman测试一下:

1. 先是测试上传正确安全的图片

springboot 上传图片到static目录_七牛云存储_03

成功啦!再去七牛云确认一下,仓库里多了一个图片文件,并且文件链接和我返回的链接是一样的,复制文件链接也可以直接打开文件。

springboot 上传图片到static目录_七牛_04

2. 再测试伪装成图片的病毒文件

对于文件的上传,一定要考虑文件的安全性!

springboot 上传图片到static目录_spring boot_05


好啦,这就是我的完成过程,如果有什么问题或者有什么想要补充的欢迎在评论里提出来,大家一起交流。