Spring Boot 应用中常常会遇到存储或处理大量文件的情况,这些文件一般不会存储在服务器本地,因为这样做的话不便于管理和分发。所以一般采用对象存储的方式进行保存。

对象存储服务(Object Storage Service,OSS)是一种海量、安全、低成本、高可靠的云存储服务,适合存放任意类型的文件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成本。

本文中采用 阿里云对象存储(OSS) 做示例,讲解 Spring 整合 OSS 相关问题。 阿里云OSS文档参考

开始前先要使用 阿里云 的账号购买 OSS 服务。获取 ossAccessKeyId 和 ossAccessKeySecret 。

 首先,pom.xml 加入 SDK 依赖:

<!-- 阿里云 对象存储OSS SDK -->
<dependency>
  <groupId>com.aliyun.oss</groupId>
  <artifactId>aliyun-sdk-oss</artifactId>
  <version>2.8.3</version>
</dependency>

然后编写 OSS 类 OSSClientUtil.java

@Slf4j
@Component
public class OSSClientUtil {

    private static final String ossAccessKeyId = "<yourAccessKeyId>";
    private static final String ossAccessKeySecret = "<yourAccessKeySecret>";
private static final String ossEndpoint = "http://oss-cn-shenzhen.aliyuncs.com"; private static final String ossBucketName = "<youBucketName>"; private OSSClient ossClient; private final FileUtil fileUtil; @Autowired public OSSClientUtil(FileUtil fileUtil) { ossClient = new OSSClient(ossEndpoint, ossAccessKeyId, ossAccessKeySecret); this.fileUtil = fileUtil; } /** * 初始化 */ public void init() { ossClient = new OSSClient(ossEndpoint, ossAccessKeyId, ossAccessKeySecret); } /** * 销毁 */ public void destory() { ossClient.shutdown(); } }

然后编写上传文件代码

/**
 * 上传到OSS服务器  如果同名文件会覆盖服务器上的
 *
 * @param instream 文件流
 * @param fileName 文件名称 包括后缀名
 * @return 出错返回"" ,唯一MD5数字签名
 */
public String uploadFile2OSS(InputStream instream, String fileName) {
    String ret = "";
    try {
        //创建上传Object的Metadata
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentLength(instream.available());
        objectMetadata.setCacheControl("no-cache");
        objectMetadata.setHeader("Pragma", "no-cache");
        objectMetadata.setContentType(getcontentType(fileName.substring(fileName.lastIndexOf("."))));
        objectMetadata.setContentDisposition("inline;filename=" + fileName);
        //上传文件
        PutObjectResult putResult = ossClient.putObject(ossBucketName, fileName, instream, objectMetadata);
        ret = putResult.getETag();
    } catch (IOException e) {
        log.error(e.getMessage(), e);
    } finally {
        try {
            if (instream != null) {
                instream.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return ret;
}

如果单纯上传图片则可以使用如下两个函数:

/**
 * 上传图片
 *
 * @param base64   图片base64(含URI)
 * @param fileName 文件名头部
 * @return 文件名
 */
public String uploadImg2Oss(String base64, String fileName) {
    // base64 转 File
    MultipartFile file = fileUtil.base64ToMultipart(base64);

    String originalFilename = file.getOriginalFilename();
    String name = fileName + originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase();
    try {
        InputStream inputStream = file.getInputStream();
        this.uploadFile2OSS(inputStream, name);
        return name;
    } catch (Exception e) {
        log.error("图片上传失败");
        return null;
    }
}

/**
 * Description: 判断OSS服务文件上传时文件的contentType
 *
 * @param FilenameExtension 文件后缀
 * @return String
 */
public String getcontentType(String FilenameExtension) {
    if (FilenameExtension.equalsIgnoreCase(".bmp")) {
        return "image/bmp";
    }
    if (FilenameExtension.equalsIgnoreCase(".gif")) {
        return "image/gif";
    }
    if (FilenameExtension.equalsIgnoreCase(".jpeg") ||
            FilenameExtension.equalsIgnoreCase(".jpg") ||
            FilenameExtension.equalsIgnoreCase(".png")) {
        return "image/jpeg";
    }
    if (FilenameExtension.equalsIgnoreCase(".html")) {
        return "text/html";
    }
    if (FilenameExtension.equalsIgnoreCase(".txt")) {
        return "text/plain";
    }
    if (FilenameExtension.equalsIgnoreCase(".vsd")) {
        return "application/vnd.visio";
    }
    if (FilenameExtension.equalsIgnoreCase(".pptx") ||
            FilenameExtension.equalsIgnoreCase(".ppt")) {
        return "application/vnd.ms-powerpoint";
    }
    if (FilenameExtension.equalsIgnoreCase(".docx") ||
            FilenameExtension.equalsIgnoreCase(".doc")) {
        return "application/msword";
    }
    if (FilenameExtension.equalsIgnoreCase(".xml")) {
        return "text/xml";
    }
    return "image/jpeg";
}

最后是获得 URL 方式:

/**
 * 获得url链接
 *
 * @param key
 * @return url
 */
public String getUrl(String key) {
    // 设置URL过期时间为10年  3600l* 1000*24*365*10
    Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 10);
    // 生成URL
    URL url = ossClient.generatePresignedUrl(ossBucketName, key, expiration);
    if (url != null) {
        return url.toString();
    }
    return null;
}

编写测试代码如下:

@PostMapping(value = "/uploadImg")
@ApiOperation(value = "上传图片到OSS")
public String uploadImg(@RequestBody ImageDto dto) {
    String[] imgs = dto.getImg().split(",");
    if (imgs.length != 2) return "图片需要含URI";

    String fileName = "Temp/" + "uploadImgTest-" + UUID.randomUUID().toString().replaceAll("-", "");
    String name = ossClientUtil.uploadImg2Oss(dto.getImg(), fileName);
    String imgUrl = ossClientUtil.getImgUrl(name);
    if (Strings.isNullOrEmpty(imgUrl) || !imgUrl.contains("?")) return "图片保存错误";
    String[] imgUrls = imgUrl.split("[?]");

    return "上传成功,图片链接: \n" + imgUrls[0];
}

@PostMapping(value = "uploadFile")
@ApiOperation(value = "上传文件到OSS")
public String uploadFile(@ApiParam(value = "上传文件", required = true) MultipartFile file) throws Exception {
    String fileName = file.getOriginalFilename().toLowerCase();
    String suffix = fileName.substring(fileName.lastIndexOf('.'));
    String name = "Temp/" + UUID.randomUUID().toString().replaceAll("-", "") + suffix;
    String fileUrl;

    ossClientUtil.uploadFile2OSS(file.getInputStream(), name);
    if (Strings.isNullOrEmpty(name)) return "上传失败";

    fileUrl = ossClientUtil.getUrl(name);
    if (!fileUrl.startsWith("http")) return "获取失败";

    return "文件上传:\n" + fileUrl.substring(0, fileUrl.indexOf('?'));
}

@GetMapping(value = "/downloadImg")
@ApiOperation(value = "下载图片")
public String downloadImg(String imgUrl) {
    String uri = "data:" + ossClientUtil.getcontentType(imgUrl.substring(imgUrl.lastIndexOf("."))) + ";base64,";
    String a = uri + imageUtils.image2Base64(imgUrl); // 稳定

    return "原图转base64: \n" + a;
}

测试:

上传图片:


上传文件:  


下载图片:


PS:本站的 图片/Base64 转换地址 http://www.wuguangnuo.cn/tool/img2base64