Springboot集成 阿里云OSS上传及下载


    为解决服务端IO压力,将文件服务转移至阿里云OSS,先丢一个 阿里云OSS官网链接。 上传及下载流程为:将文件上传至OSS,并拿到文件对应的key。根据获取key,再从OSS获取文件临时URL(安全性考虑,加上了url有效期)。

    突然想起好久没写博客了,正好上来记录一二~ 关于代码中的oss配置,想着写成一个统一配置bean,这样就不用在每一个使用的地方都进行设值,但由于本人太懒了等后期再倒腾吧~

使用手册

Step1:加入maven依赖及配置文件中配置秘钥等
Step2:定义配置bean与OSS工具类
Step3:定义UploadController和DownloadController

maven依赖及环境配置

  1. 在pom.xml中加入依赖:
<dependency>
   <groupId>com.alibaba.cloud</groupId>
   <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
</dependency>
  1. 在bootstraps.yml(或application.yml等)中配置阿里云秘钥等
alicloud: 
  access-key: xxxxx
  secret-key: xxxx
  oss: 
    endpoint: oss-cn-hangzhou.aliyuncs.com

定义配置bean及OSS工具类

  1. 声明配置bean
/**
 * 阿里云OSS配置
 */
@Data
public class AliyunOssItem {
	
	private String accessKey;
	
	private String sceretKey;
	
	private String endpoint;
	
	/**
	 *  存储空间名称 
	 */
	private String bucketName;
	
	/**
	 * 上传目录
	 */
	private String uploadDir;
	
	/**
	 * 
	 * @param accessKey
	 * @param sceretKey
	 * @param endpoint 
	 * @param bucketName 存储空间名称
	 * @param uploadDir 上传目录
	 */
	public AliyunOssItem(String accessKey, String sceretKey, String endpoint, String bucketName, String uploadDir) {
		
		this.accessKey = accessKey;
		this.sceretKey = sceretKey;
		this.endpoint = endpoint;
		this.bucketName = bucketName;
		this.uploadDir = uploadDir;
	}
	
	/**
	 * 
	 * @param accessKey
	 * @param sceretKey
	 * @param endpoint 
	 * @param bucketName 存储空间名称
	 */
	public AliyunOssItem(String accessKey, String sceretKey, String endpoint, String bucketName) {
		
		this.accessKey = accessKey;
		this.sceretKey = sceretKey;
		this.endpoint = endpoint;
		this.bucketName = bucketName;
	}
}
  1. OSS工具类
/**
 * 阿里云OSS上传和下载文件工具
 */
@Slf4j
@UtilityClass
public class AliyunOssUtil {

	/**
	 * 返回文件在OSS存储的key 
	 * @param file 上传文件
	 * @param item OSS配置bean
	 * @param ossClient 
	 * @return
	 */
	public static String upload2OSS(MultipartFile file, AliyunOssItem item) {
		
		OSS ossClient = new OSSClientBuilder().build(item.getEndpoint(), item.getAccessKey(), item.getSceretKey());
		
		String bucketName = item.getBucketName();
		String fileName = file.getOriginalFilename();// 文件名
		String uploadDir = item.getUploadDir();// 目录名
		String uploadPath = "";// 保存文件路径名称
		InputStream uploadInputStrem = null;
		if (StringUtils.isNotEmpty(uploadDir)) {
			uploadDir = uploadDir.substring(0, uploadDir.length()).replaceAll("\\\\", "/") + "/";
		}
		
		try {
			ensureBucket(item.getBucketName(), ossClient);
			// 获取上传文件后缀名
			String fileSuffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
			
			String md5 = MD5.getMessageDigest(file.getBytes());
			uploadPath = String.format("%1$s%2$s%3$s", uploadDir, md5, fileSuffix);
			
			// 创建上传Object的Metadata。ObjectMetaData是用户对该object的描述
			ObjectMetadata objectMetadata = new ObjectMetadata();
			objectMetadata.setContentLength(file.getSize());
			objectMetadata.setCacheControl("no-cache");
			objectMetadata.setContentEncoding("utf-8");
			objectMetadata.setContentType(getcontentType(file, fileSuffix));// 获取文件类型
			objectMetadata.setContentDisposition("attachment;filename=" + fileName + fileSuffix);
			uploadInputStrem = file.getInputStream(); // 文件输入流
			// 上传文件
			log.debug("正在上传文件到OSS...");
			ossClient.putObject(bucketName, uploadPath, uploadInputStrem, objectMetadata);
			log.debug("上传文件完成...");
			return uploadPath;
			
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			try {
				if (uploadInputStrem != null) {
					uploadInputStrem.close();// 关闭文件流
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			// 关闭OSSClient。
			ossClient.shutdown();
		}
		return null;
	}

	/**
	 * 判断bucket存储空间是否已建立 若未建立,先创建一个Bucket
	 */
	private void ensureBucket(String bucketName, OSS ossClient) throws OSSException {
		// 判断bucket是否存在
		boolean exists = ossClient.doesBucketExist(bucketName);
		if (!exists) {
			// log.error("bucket不存在,新建当前bucket:{}",BUCKETNAME);
			ossClient.createBucket(bucketName);
		}
	}
	
	/**
	 * 根据key,从OSS上获取图片临时url
	 * @param item OSS配置
	 * @param key 
	 * @param expiration 过期时间(单位s)
	 * @return
	 */
	public String downFromOSS(AliyunOssItem item, String key, int expiration) {
		
		OSS ossClient = new OSSClientBuilder().build(item.getEndpoint(), item.getAccessKey(), item.getSceretKey());
		
		try {
			if (StringUtils.isNotEmpty(key)) {
				// 设置URL过期时间
				Date expirationDate = new Date(System.currentTimeMillis() + expiration);
				// 生成URL
				URL url = ossClient.generatePresignedUrl(item.getBucketName(), key, expirationDate);
				if (null != url) {
					return url.toString();
				}
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			//关闭
			ossClient.shutdown();
		}
		
		return null;
	}

	/**
	 * 判断OSS服务文件上传时文件的contentType
	 *
	 * @param file              上传文件
	 * @param FilenameExtension 文件后缀
	 * @return String
	 */
	private String getcontentType(MultipartFile file, 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") || FilenameExtension.equalsIgnoreCase(".jpz")) {
			return "image/jpeg";
		}
		if (FilenameExtension.equalsIgnoreCase(".html") || FilenameExtension.equalsIgnoreCase(".htm")
				|| FilenameExtension.equalsIgnoreCase(".hts")) {
			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";
		}
		if (FilenameExtension.equalsIgnoreCase(".xls")) {
			return "application/vnd.ms-excel";
		}
		if (FilenameExtension.equalsIgnoreCase(".xlsx")) {
			return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
		}
		if (FilenameExtension.equalsIgnoreCase(".zip")) {
			return "application/zip";
		}
		return file.getContentType();
	
}

定义UploadController和DownloadController

  1. 定义UploadController
/**
 * 后台文件上传接口
 *
 */
@Slf4j
@Controller
public class UploadController {
	
	/** 存储空间名称 */
	private static final String BUCKETNAME = "bucket-private";

	private static final String ROOT_DIR = "root";
	
	@Value("${spring.cloud.alicloud.access-key}")
	private String accessKeyId;
	
	@Value("${spring.cloud.alicloud.secret-key}")
	private String sceretKey;
	
	@Value("${spring.cloud.alicloud.oss.endpoint}")
	private String endpoint;
	
	/**
	 * 上传至OSS
	 * @param file
	 * @return
	 * @throws Exception
	 */
	@ResponseBody
	@PostMapping({ "/upload2OSS"})
	public ResponseVo<?> upload2OSS(@RequestParam(value = "file", required = false) MultipartFile file) throws Exception {
		
		if (null == file || file.isEmpty()) {
			throw new UploadFileNotFoundException(UploadResponseVo.Error.FILENOTFOUND);
		}
		
		try {
			SimpleDateFormat fmt = new SimpleDateFormat("yyyy/MM/dd");
			String dir = fmt.format(new Date());
			
			AliyunOssItem item = new AliyunOssItem(accessKeyId, sceretKey, endpoint, BUCKETNAME, ROOT_DIR + "/" + dir);
			String url = AliyunOssUtil.upload2OSS(file, item);
			if (!StringUtils.isEmpty(url)) {
				log.debug("上传文件至OSS后的url为:" + url);
				return ResultUtil.success("上传成功", url);
			}
			
		} catch (Exception e) {
			log.error(String.format("UploadController.upload%s", e));
			throw e;
		}
		return ResultUtil.error("上传失败!");
	}
}
  1. 定义DownloadController
/**
 * 后台文件下载接口
 *
 */
@Slf4j
@Controller
public class DownloadController {
	
	/** 存储空间名称 */
	private static final String BUCKETNAME = "ak-web-private";
	
	@Value("${spring.cloud.alicloud.access-key}")
	private String accessKeyId;
	
	@Value("${spring.cloud.alicloud.secret-key}")
	private String sceretKey;
	
	@Value("${spring.cloud.alicloud.oss.endpoint}")
	private String endpoint;
	
	@Value("${spring.cloud.alicloud.oss.expiration}")
	private Integer expiration;
	
	@ResponseBody
	@PostMapping({ "/download"})
	public ResponseVo<?> downloadFromOSS(String key) throws Exception {
		
		try {
			
			AliyunOssItem item = new AliyunOssItem(accessKeyId, sceretKey, endpoint, BUCKETNAME);
			String fileTmpUrl = AliyunOssUtil.downFromOSS(item, key, expiration);
			if (!StringUtils.isEmpty(fileTmpUrl)) {
				return ResultUtil.success("下载成功", fileTmpUrl);
			}
			
		} catch (Exception e) {
			log.error(String.format("DownloadController.downloadFromOSS%s", e));
			throw e;
		}
		return ResultUtil.error("下载失败!");
	}
}

下载接口优化为返回重定向oss路径

/**
 * 后台文件下载接口
 *
 */
@Slf4j
@Controller
public class DownloadController {
	
	/** 存储空间名称 */
	private static final String BUCKETNAME = "ak-web-private";
	
	@Value("${spring.cloud.alicloud.access-key}")
	private String accessKeyId;
	
	@Value("${spring.cloud.alicloud.secret-key}")
	private String sceretKey;
	
	@Value("${spring.cloud.alicloud.oss.endpoint}")
	private String endpoint;
	
	@Value("${spring.cloud.alicloud.oss.expiration}")
	private Integer expiration;
	
	@PostMapping({ "/download"})
	public String downloadFromOSS(String key) throws Exception {
		
		try {
			
			AliyunOssItem item = new AliyunOssItem(accessKeyId, sceretKey, endpoint, BUCKETNAME);
			String fileTmpUrl = AliyunOssUtil.downFromOSS(item, key, expiration);
			if (!StringUtils.isEmpty(fileTmpUrl)) {
				return ResultUtil.success("下载成功", fileTmpUrl);
			}
			return "redirect:" + fileTmpUrl;
		} catch (Exception e) {
			log.error(String.format("DownloadController.downloadFromOSS%s", e));
			throw e;
		}
	}
}

遇到的问题

The bucket you visit is not belong to you

在网上找到的解决方案有:
第一种解决办法:url中主域名后面增加bucket目录。
第二种解决办法:在bucket管理内增加安全访问域名

但是我遇到的问题是由创建的OSS上传路径中携带了"//" 造成的~~

从OSS获取临时url是http协议的

查看ClientConfiguration源码,发现默认protocol是HTTP

public class ClientConfiguration {
	... 
	protected Protocol protocol = Protocol.HTTP;
}

    但是因为客户端有微信小程序,规定访问的资源必须为https协议的,所以需要将临时url变更为https协议

解决方案:知道是配置的锅,当然得从配置上下功夫喽,所以创建OSSClient时加入配置!

/**
* 获取OSSClient
* @param item
* @return
*/
private OSS getOssClient(AliyunOssItem item) {
		
	ClientBuilderConfiguration config = new ClientBuilderConfiguration();
	config.setProtocol(Protocol.HTTPS);
	return new OSSClientBuilder().build(item.getEndpoint(), item.getAccessKey(), item.getSceretKey(), config);
}
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值