SpringBoot 2.X 集成阿里云 OSS 文件上传功能

1 摘要

阿里云作为一个云服务提供商,其提供的稳定、完善的服务让阿里云在国内市场占据相当高的市场份额。众多小型创业公司将阿里云作为服务器的首选。本文将介绍如何在 Spring Boot 项目中集成阿里云的 OSS 文件上传功能。

阿里云 OSS 官方文档: https://help.aliyun.com/document_detail/31947.html

2 Maven 依赖

../pom.xml
../demo-common/pom.xml
            <!-- aliYun oss -->
            <dependency>
                <groupId>com.aliyun.oss</groupId>
                <artifactId>aliyun-sdk-oss</artifactId>
                <version>${aliyun.oss.version}</version>
            </dependency>

其中对应的版本为:

<aliyun.oss.version>3.4.2</aliyun.oss.version>

3 配置与使用阿里云 oss

3.1 配置阿里云 oss
../demo-common/src/main/java/com/ljq/demo/springboot/common/config/OSSConfig.java
package com.ljq.demo.springboot.common.config;

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

import java.io.Serializable;

/**
 * @Description: 阿里云 OSS 配置信息
 * @Author: junqiang.lu
 * @Date: 2019/5/9
 */
@Data
@Configuration
public class OSSConfig implements Serializable {

    private static final long serialVersionUID = -119396871324982279L;

    /**
     * 阿里云 oss 站点
     */
    @Value("${oss.endpoint}")
    private String endpoint;

    /**
     * 阿里云 oss 资源访问 url
     */
    @Value("${oss.url}")
    private String url;

    /**
     * 阿里云 oss 公钥
     */
    @Value("${oss.accessKeyId}")
    private String accessKeyId;

    /**
     * 阿里云 oss 私钥
     */
    @Value("${oss.accessKeySecret}")
    private String accessKeySecret;

    /**
     * 阿里云 oss 文件根目录
     */
    @Value("${oss.bucketName}")
    private String bucketName;


}

yml 中配置阿里云 oss 信息:

../demo-web/src/main/resources/application-dev.yml
../demo-web/src/main/resources/application-test.yml
../demo-web/src/main/resources/application-pro.yml
## aliyun oss
## 配置说明参考: com.ljq.demo.springboot.common.config.OSSConfig.class
oss:
  endpoint: oss-cn-hangzhou.aliyuncs.com
  url: https://xxxx.oss-cn-hangzhou.aliyuncs.com/
  accessKeyId: your_accessKey_id
  accessKeySecret: your_accessKey_secret
  bucketName: your_bucket_name

3.2 阿里云 OSS 文件上传工具类

这里作者写了两个类,作用是相同的,都是用于上传文件至阿里云 OSS。不同之处在意一个是使用较少依赖的版本,另一个是使用较多依赖的版本(必须搭配上边的 oss 配置文件类以及 Spring Boot)

低依赖版本:

../demo-common/src/main/java/com/ljq/demo/springboot/common/util/OSSSingleUtil.java
package com.ljq.demo.springboot.common.util;

import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;

import java.io.InputStream;
import java.util.UUID;

/**
 * @Description: 阿里云 oss 上传工具类(低依赖版)
 * @Author: junqiang.lu
 * @Date: 2019/5/9
 */
public class OSSSingleUtil {

    private OSSSingleUtil(){

    }

    /**
     * oss 工具客户端
     */
    private volatile static OSSClient ossClient = null;

    /**
     *  上传文件至阿里云 oss
     *
     * @param ossEndpoint 阿里云 oss 站点
     * @param ossAccessKeyId 阿里云 oss 公钥
     * @param ossAccessKeySecret 阿里云 oss 私钥
     * @param bucketName 阿里云 oss bucket 名称
     * @param ossUrl 阿里云资源访问 url (不包含具体资源名称部分)
     * @param inputStream 待上传文件流
     * @param fileDir 待上传文件在阿里云 oss 中保存的目录
     * @param fileSuffix 待上传文件类型后缀名(eg: png,doc)
     * @return
     */
    public static String upload(String ossEndpoint, String ossAccessKeyId, String ossAccessKeySecret, String bucketName,
                                String ossUrl, InputStream inputStream, String fileDir, String fileSuffix) {
        initOSS(ossEndpoint, ossAccessKeyId, ossAccessKeySecret);
        try {
            StringBuilder fileUrl = new StringBuilder();
            String point = ".";
            if (!fileSuffix.startsWith(point)) {
                fileSuffix = point + fileSuffix;
            }
            if (!fileDir.endsWith("/")) {
                fileDir = fileDir.concat("/");
            }
            String ossFileName = System.currentTimeMillis() + "-" + UUID.randomUUID().toString().substring(0,18) + fileSuffix;
            fileUrl = fileUrl.append(fileDir + ossFileName);
            ossClient.putObject(bucketName, fileUrl.toString(), inputStream);
            fileUrl = fileUrl.insert(0, ossUrl);
            return fileUrl.toString();
        } catch (OSSException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 初始化 oss 客户端
     *
     * @param ossEndpoint
     * @param ossAccessKeyId
     * @param ossAccessKeySecret
     * @return
     */
    private static OSSClient initOSS(String ossEndpoint, String ossAccessKeyId, String ossAccessKeySecret) {
        if (ossClient == null ) {
            synchronized (OSSSingleUtil.class) {
                if (ossClient == null) {
                    ossClient = new OSSClient(ossEndpoint,
                            new DefaultCredentialProvider(ossAccessKeyId, ossAccessKeySecret),
                            new ClientConfiguration());
                }
            }
        }
        return ossClient;
    }

}

高依赖版本:

../demo-common/src/main/java/com/ljq/demo/springboot/common/util/OSSBootUtil.java
package com.ljq.demo.springboot.common.util;

import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.ljq.demo.springboot.common.config.OSSConfig;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.UUID;

/**
 * @Description: 阿里云 oss 上传工具类(高依赖版)
 * @Author: junqiang.lu
 * @Date: 2019/5/10
 */
public class OSSBootUtil {

    private OSSBootUtil(){}

    /**
     * oss 工具客户端
     */
    private volatile static OSSClient ossClient = null;

    /**
     * 上传文件至阿里云 OSS
     * 文件上传成功,返回文件完整访问路径
     * 文件上传失败,返回 null
     *
     * @param ossConfig oss 配置信息
     * @param file 待上传文件
     * @param fileDir 文件保存目录
     * @return oss 中的相对文件路径
     */
    public static String upload(OSSConfig ossConfig, MultipartFile file, String fileDir){
        initOSS(ossConfig);
        StringBuilder fileUrl = new StringBuilder();
        try {
            String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf('.'));
            String fileName = System.currentTimeMillis() + "-" + UUID.randomUUID().toString().substring(0,18) + suffix;
            if (!fileDir.endsWith("/")) {
                fileDir = fileDir.concat("/");
            }
            fileUrl = fileUrl.append(fileDir + fileName);

            ossClient.putObject(ossConfig.getBucketName(), fileUrl.toString(), file.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        fileUrl = fileUrl.insert(0,ossConfig.getUrl());
        return fileUrl.toString();
    }

    /**
     * 初始化 oss 客户端
     * @param ossConfig
     * @return
     */
    private static OSSClient initOSS(OSSConfig ossConfig) {
        if (ossClient == null ) {
            synchronized (OSSBootUtil.class) {
                if (ossClient == null) {
                    ossClient = new OSSClient(ossConfig.getEndpoint(),
                            new DefaultCredentialProvider(ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret()),
                            new ClientConfiguration());
                }
            }
        }
        return ossClient;
    }
}

3.3 使用工具类

Service 实现类

../demo-service/src/main/java/com/ljq/demo/springboot/service/impl/CommonServiceImpl.java
package com.ljq.demo.springboot.service.impl;

import com.ljq.demo.springboot.common.api.ApiResult;
import com.ljq.demo.springboot.common.config.OSSConfig;
import com.ljq.demo.springboot.common.config.PDFExportConfig;
import com.ljq.demo.springboot.common.util.FileUtil;
import com.ljq.demo.springboot.common.util.OSSBootUtil;
import com.ljq.demo.springboot.common.util.OSSSingleUtil;
import com.ljq.demo.springboot.common.util.PDFUtil;
import com.ljq.demo.springboot.service.CommonService;
import com.ljq.demo.springboot.vo.DownloadBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description: 公共业务具体实现类
 * @Author: junqiang.lu
 * @Date: 2018/12/24
 */
@Service("commonService")
public class CommonServiceImpl implements CommonService {

    @Autowired
    private PDFExportConfig pdfExportConfig;
    @Autowired
    private OSSConfig ossConfig;

    /**
     * 上传文件至阿里云 oss
     *
     * @param file
     * @param uploadKey
     * @return
     * @throws Exception
     */
    @Override
    public ApiResult uploadOSS(MultipartFile file, String uploadKey) throws Exception {

        // 低依赖版本 oss 上传工具
        String fileSuffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf('.'));;
        String ossFileUrlSingle = null;
        ossFileUrlSingle = OSSSingleUtil.upload(ossConfig.getEndpoint(), ossConfig.getAccessKeyId(),
                ossConfig.getAccessKeySecret(), ossConfig.getBucketName(), ossConfig.getUrl(), file.getInputStream(),
                "upload/demo", fileSuffix);

        // 高依赖版本 oss 上传工具
        String ossFileUrlBoot = null;
        ossFileUrlBoot = OSSBootUtil.upload(ossConfig, file, "upload/demo");

        Map<String, Object> resultMap = new HashMap<>(16);
        resultMap.put("ossFileUrlSingle", ossFileUrlSingle);
        resultMap.put("ossFileUrlBoot", ossFileUrlBoot);

        return ApiResult.success(resultMap);
    }
}

其他流程类:

Service 接口:

../demo-service/src/main/java/com/ljq/demo/springboot/service/CommonService.java
package com.ljq.demo.springboot.service;

import com.ljq.demo.springboot.common.api.ApiResult;
import com.ljq.demo.springboot.vo.DownloadBean;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;

/**
 * @Description: 公共业务
 * @Author: junqiang.lu
 * @Date: 2018/12/24
 */
public interface CommonService {

    /**
     * 上传文件至阿里云 oss
     *
     * @param file
     * @param uploadKey
     * @return
     * @throws Exception
     */
    ApiResult uploadOSS(MultipartFile file, String uploadKey) throws Exception;


}

控制层 Controller:

../demo-web/src/main/java/com/ljq/demo/springboot/web/controller/CommonController.java
package com.ljq.demo.springboot.web.controller;

import com.ljq.demo.springboot.common.api.ApiResult;
import com.ljq.demo.springboot.service.CommonService;
import com.ljq.demo.springboot.vo.DownloadBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

/**
 * @Description: 公共模块控制中心
 * @Author: junqiang.lu
 * @Date: 2018/12/24
 */
@RestController
@RequestMapping("api/demo/common")
public class CommonController {

    private static final Logger logger = LoggerFactory.getLogger(CommonController.class);

    @Autowired
    private CommonService commonService;


    /**
     * 上传文件至阿里云 oss
     *
     * @param file
     * @param uploadKey
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/upload/oss", method = {RequestMethod.POST}, produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<?> uploadOSS(@RequestParam(value = "file") MultipartFile file, String uploadKey) throws Exception {
        ApiResult apiResult = commonService.uploadOSS(file, uploadKey);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        return new ResponseEntity<>(apiResult, headers, HttpStatus.CREATED);
    }


}

4 测试

测试工具: Postman

POST /api/demo/common/upload/oss
Content-Type: multipart/form-data
User-Agent: PostmanRuntime/7.11.0
Accept: */*
Cache-Control: no-cache
Postman-Token: ac18d0b0-3ffe-432c-9f9e-46f17f8bb788
Host: 127.0.0.1:8848
accept-encoding: gzip, deflate
content-length: 71107
Connection: keep-alive

file=[object Object]uploadKey=uploadValue


HTTP/1.1 201
status: 201
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 13 May 2019 12:31:39 GMT
{"code":1000,"msg":"成功","data":{"ossFileUrlSingle":"https://eduhkyx.oss-cn-hangzhou.aliyuncs.com/upload/demo1557750699120-0e1a9421-b14b-4cf2.png","ossFileUrlBoot":"https://eduhkyx.oss-cn-hangzhou.aliyuncs.com/upload/demo1557750699167-8960c1aa-34aa-4121.png"}}

Postman测试截图

后台日志:

2019-05-13 20:31:39:120 [http-nio-8848-exec-5] INFO  com.ljq.demo.springboot.web.acpect.LogAspect(LogAspect.java 66) -[AOP-LOG-START]
	requestMark: 864f62b1-2687-43f6-9421-43ae3aa6830a
	requestIP: 127.0.0.1
	contentType:multipart/form-data; boundary=--------------------------604023971854029081739034
	requestUrl: http://127.0.0.1:8848/api/demo/common/upload/oss
	requestMethod: POST
	requestParams: uploadKey = uploadValue;fileSize = 70781;fileContentType = image/png;fieldName = file;fileOriginalName = google.png;
	targetClassAndMethod: com.ljq.demo.springboot.web.controller.CommonController#uploadOSS
2019-05-13 20:31:39:225 [http-nio-8848-exec-5] INFO  com.ljq.demo.springboot.web.acpect.LogAspect(LogAspect.java 74) -[AOP-LOG-END]
	<201 Created,ApiResult(code=1000, msg=成功, data={ossFileUrlSingle=https://eduhkyx.oss-cn-hangzhou.aliyuncs.com/upload/demo1557750699120-0e1a9421-b14b-4cf2.png, ossFileUrlBoot=https://eduhkyx.oss-cn-hangzhou.aliyuncs.com/upload/demo1557750699167-8960c1aa-34aa-4121.png}),{Content-Type=[application/json;charset=UTF-8]}>

5 Github 源码

Gtihub 源码地址 : https://github.com/Flying9001/springBootDemo

个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.
404Code

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值