文章目錄
- 使用手冊
-
- maven依賴及環境配置
- 定義配置bean及OSS工具類
- 定義UploadController和DownloadController
-
- 下載下傳接口優化為傳回重定向oss路徑
- 遇到的問題
-
- The bucket you visit is not belong to you
- 從OSS擷取臨時url是http協定的
為解決服務端IO壓力,将檔案服務轉移至阿裡雲OSS,先丢一個 阿裡雲OSS官網連結。 上傳及下載下傳流程為:将檔案上傳至OSS,并拿到檔案對應的key。根據擷取key,再從OSS擷取檔案臨時URL(安全性考慮,加上了url有效期)。
突然想起好久沒寫部落格了,正好上來記錄一二~ 關于代碼中的oss配置,想着寫成一個統一配置bean,這樣就不用在每一個使用的地方都進行設值,但由于本人太懶了等後期再倒騰吧~
使用手冊
Step1:加入maven依賴及配置檔案中配置秘鑰等
Step2:定義配置bean與OSS工具類
Step3:定義UploadController和DownloadController
maven依賴及環境配置
- 在pom.xml中加入依賴:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-oss</artifactId>
</dependency>
- 在bootstraps.yml(或application.yml等)中配置阿裡雲秘鑰等
alicloud:
access-key: xxxxx
secret-key: xxxx
oss:
endpoint: oss-cn-hangzhou.aliyuncs.com
定義配置bean及OSS工具類
- 聲明配置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;
}
}
- 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
- 定義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("上傳失敗!");
}
}
- 定義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);
}