OSS阿里雲端儲存——整合Spring檔案上傳,實現動靜網站資源分離
寫在前面
在日常學習或開發java web專案的過程中,難免會遇到各種各樣的檔案上傳問題。傳統做法或固有思路,一向是把這些使用者上傳的靜態資源儲存在自己的伺服器中。這也是初學者普遍的做法。但是,長此以往,大量使用者累積的使用者靜態資原始檔,勢必會大量佔用伺服器的儲存空間,降低伺服器的效能,同時也增加了網站維護的成本。
於是,另一種做法營運而生,就是把網站的靜態資源和動態資源實現分離。增加一臺伺服器專門用來儲存靜態檔案,這不失為一種好辦法。但小編今天分享的另一種方法是在第三方雲端儲存伺服器的基礎上,實現自己的檔案上傳以及檔案雲端儲存,達到網站資源動靜分離的目的。
關於阿里雲OSS雲端儲存伺服器的具體資料和相關文件以及各個語言的API請讀者自行百度或瀏覽官網學習,這裡就不做過多的贅述。傳送門:
以下小編主要分享Spring+OSS雲端儲存服務上傳檔案的具體操作。
準備工作
1.阿里雲賬號
2.一臺阿里雲端儲存伺服器(包半年6.00元左右,購買以及基本配置請參考官網)
開發環境以及工具
1.java 1.8
2.idea
3.Tomcat9 主要用來測試
4.maven3.5
你必須知道的OSS物件伺服器的基本概念
1.儲存空間(Bucket)
儲存空間是您用於儲存物件(Object)的容器,所有的物件都必須隸屬於某個儲存空間。
2.物件/檔案(Object)
物件是 OSS 儲存資料的基本單元,也被稱為 OSS 的檔案
3.Region(地域)
Region 表示 OSS 的資料中心所在的地域,物理位置。
4.Endpoint(訪問域名)
Endpoint 表示 OSS 對外服務的訪問域名。
5.AccessKey(訪問金鑰)
AccessKey,簡稱 AK,指的是訪問身份驗證中用到的 AccessKeyId 和AccessKeySecret。OSS 通過使用 AccessKeyId 和 AccessKeySecret 對稱加密的方法來驗證某個請求的傳送者身份。AccessKeyId 用於標識使用者,AccessKeySecret 是使用者用於加密簽名字串和 OSS 用來驗證簽名字串的金鑰,其中 AccessKeySecret 必須保密。對於 OSS 來說,AccessKey 的來源有:
Bucket 的擁有者申請的 AccessKey。
被 Bucket 的擁有者通過 RAM 授權給第三方請求者的 AccessKey。
被 Bucket 的擁有者通過 STS 授權給第三方請求者的 AccessKey。
以上列出的只是部分基礎的概念,詳情請參考官方文件。
專案組織結構
此專案本來是一個web專案,用到的資料庫是mongodb,主要用來記錄介面訪問日誌。化紅線的是上傳檔案的主要功能類。因此,其他配置檔案中的具體配置在此不詳加贅述,這裡只提供基本的思路。更加完美的功能請自行完善。
OSS 介面的配置使用和基本操作封裝
使用前匯入maven依賴
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>2.7.0</version>
</dependency>
AliOSSConfig.java
/**
* @author longping jie
* @name AliOSSConfig
* @description the class is 阿里雲oos上傳客戶端物件的配置類
* @date 2017/9/20
*/
public class AliOSSConfig {
public static String END_POINT;
public static String ACCESS_KEY_ID;
public static String ACCESS_KEY_SECRET;
public static int VISIT_GRADE = 3;
static {
//外部接入入口
END_POINT = PropertiesHelper.getValueByKey("END_POINT");
//祕鑰id
ACCESS_KEY_ID = PropertiesHelper.getValueByKey("ACCESS_KEY_ID");
//祕鑰對應該是
ACCESS_KEY_SECRET = PropertiesHelper.getValueByKey("ACCESS_KEY_SECRET");
}
}
這些常量配置從properties配置檔案中讀取,properties檔案和其操作的封裝自行處理。
AliOSSBaseTool.java 封裝一些OSS的基本操作
package com.longge.upload.util;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.CreateBucketRequest;
import com.aliyun.oss.model.OSSObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.ByteArrayInputStream;
/**
* @author longping jie
* @name AliOSSTool
* @description the class is 阿里雲OSS客戶端工具類
* @date 2017/9/20
*/
public class AliOSSBaseTool {
static Logger logger = LogManager.getLogger(AliOSSBaseTool.class);
/**
* 判斷一個儲存空間是否存在
*
* @param ossClient
* @param bucketName
* @return
*/
public static boolean doesBucketExits(OSSClient ossClient, String bucketName) {
return ossClient.doesBucketExist(bucketName);
}
/**
* 建立一個儲存空間
* 並給儲存空間對外賦讀寫許可權
* @param ossClient oss連線
* @param bucketName 儲存空間名字
* @param grade 訪問等級(1,2,3)(私有,公共讀,公共讀寫)
* @return 是否建立成功
*/
public static boolean createBuckName(OSSClient ossClient, String bucketName, int grade) {
boolean res;
if (doesBucketExits(ossClient, bucketName)) return true;
CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
switch (grade) {
case 1:
createBucketRequest.setCannedACL(CannedAccessControlList.Private);
break;
case 2:
createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
break;
case 3:
createBucketRequest.setCannedACL(CannedAccessControlList.PublicReadWrite);
break;
default:
createBucketRequest.setCannedACL(CannedAccessControlList.Private);
break;
}
ossClient.createBucket(createBucketRequest);
res = true;
return res;
}
/**
* 刪除一個儲存空間
*
* @param ossClient oss連線
* @param bucketName 儲存空間名字
*/
public void deleteBucketName(OSSClient ossClient, String bucketName) {
if (doesBucketExits(ossClient, bucketName)) {
ossClient.deleteBucket(bucketName);
}
}
/**
* 建立一個資料夾
*
* @param ossClient oss連線
* @param bucketName 儲存空間名字
* @param folder 資料夾名
* @return true 建立成功 false資料夾已經存在
*/
public static boolean createFolder(OSSClient ossClient, String bucketName, String folder) {
if (!ossClient.doesObjectExist(bucketName, folder)) {
ossClient.putObject(bucketName, folder, new ByteArrayInputStream(new byte[0]));
OSSObject object = ossClient.getObject(bucketName, folder);
String fileDir = object.getKey();
logger.debug("資料夾建立成功:{}", fileDir);
return true;
}
return false;
}
/**
* 刪除一個資料夾
*
* @param ossClient oss連線
* @param bucketName 儲存空間名字
* @param folder 資料夾名
* @return true 刪除成功 false資料夾已經存在
*/
public static boolean deleteFolder(OSSClient ossClient, String bucketName, String folder) {
if (ossClient.doesObjectExist(bucketName, folder)) {
ossClient.deleteObject(bucketName, folder);
logger.debug("刪除資料夾成功:{}", folder);
return true;
}
return false;
}
/**
* 根據key刪除OSS伺服器上的檔案
* @param ossClient oss連線
* @param bucketName 儲存空間
* @param folder 模擬資料夾名
* @param key Bucket下的檔案路徑名+檔名 如:"upload/cake.jpg"
*/
public static void deleteFile(OSSClient ossClient,String bucketName,String folder,String key){
ossClient.deleteObject(bucketName,folder+key);
logger.debug("刪除:{}",bucketName+"下的檔案:{}",folder+key+"成功!");
}
}
AliOSSApplayUtil.java OOS應用工具類,封裝圖片上傳
package com.longge.upload.util;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.*;
import java.util.Date;
import java.util.UUID;
/**
* @author longping jie
* @name AliOSSUtil
* @description the class is 阿里雲應用工具類
* @date 2017/9/18
*/
public class AliOSSApplayUtil {
private final static Logger log = LogManager.getLogger(AliOSSApplayUtil.class);
private static String END_POINT=AliOSSConfig.END_POINT;
private static String ACCESS_KEY_ID=AliOSSConfig.ACCESS_KEY_ID;
private static String ACCESS_KEY_SECRET=AliOSSConfig.ACCESS_KEY_SECRET;
/**
* 獲得oss連線物件
* @return oss連線物件
*/
private static OSSClient ossClient(){
return new OSSClient(END_POINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
}
/**
* 上傳檔案
*
* @param file 檔案物件
* @return 儲存檔案在oos伺服器上的路徑,上傳成功後返回檔案的儲存路徑是一個url連線
* 直接指向雲上的資源位置
*/
public static String upload(File file,String bucketName,String folder) {
if (file == null) {
return null;
}
//建立OSS客戶端
OSSClient ossClient = ossClient();
try (InputStream is = new FileInputStream(file)) {
//判斷檔案容器是否存在,不存在則建立
AliOSSBaseTool.createBuckName(ossClient,bucketName,AliOSSConfig.VISIT_GRADE);
AliOSSBaseTool.createFolder(ossClient,bucketName,folder);
//檔名
String fileName = file.getName();
//檔案大小
Long fileSize = file.length();
//建立上傳檔案的Metadata
ObjectMetadata metadata = new ObjectMetadata();
//上傳檔案的長度
metadata.setContentLength(is.available());
//指定該object被下載時的網頁的快取行為
metadata.setCacheControl("no-cache");
//指定該object下設定Header
metadata.setHeader("Pragma","no-cache");
//指定該object被下載時的內容編碼方式
metadata.setContentEncoding("utf-8");
//檔案的MIME,定義檔案的型別及網頁編碼,決定瀏覽器將以什麼形式、什麼編碼讀取檔案。如果使用者沒有指定則根據Key或檔名的副檔名生成,
//如果沒有副檔名則填預設值application/octet-stream
metadata.setContentType(getContentType(fileName));
//指定該Object被下載時的名稱(指示MINME使用者代理如何顯示附加的檔案,開啟或下載,及檔名稱)
metadata.setContentDisposition("filename/filesize=" + fileName + "/" + fileSize + "Byte.");
//上傳檔案 (上傳檔案流的形式)
//此處上傳檔案
PutObjectResult putResult = ossClient.putObject(bucketName, folder+fileName, is, metadata);
String fileHost = "http://"+bucketName+"."+END_POINT;
if (null != putResult) {
return fileHost+"/"+folder+fileName;
}
} catch (OSSException oe) {
oe.printStackTrace();
} catch (ClientException e) {
e.printStackTrace();
}catch (Exception e){
e.printStackTrace();
}finally {
// 關閉OSS服務,一定要關閉
ossClient.shutdown();
}
return null;
}
/**
* 通過檔名判斷並獲取OSS服務檔案上傳時檔案的contentType
* @param fileName 檔名
* @return 檔案的contentType
*/
public static String getContentType(String fileName){
//檔案的字尾名
String fileExtension = fileName.substring(fileName.lastIndexOf("."));
if(".bmp".equalsIgnoreCase(fileExtension)) {
return "image/bmp";
}
if(".gif".equalsIgnoreCase(fileExtension)) {
return "image/gif";
}
if(".jpeg".equalsIgnoreCase(fileExtension) || ".jpg".equalsIgnoreCase(fileExtension) || ".png".equalsIgnoreCase(fileExtension) ) {
return "image/jpeg";
}
if(".html".equalsIgnoreCase(fileExtension)) {
return "text/html";
}
if(".txt".equalsIgnoreCase(fileExtension)) {
return "text/plain";
}
if(".vsd".equalsIgnoreCase(fileExtension)) {
return "application/vnd.visio";
}
if(".ppt".equalsIgnoreCase(fileExtension) || "pptx".equalsIgnoreCase(fileExtension)) {
return "application/vnd.ms-powerpoint";
}
if(".doc".equalsIgnoreCase(fileExtension) || "docx".equalsIgnoreCase(fileExtension)) {
return "application/msword";
}
if(".xml".equalsIgnoreCase(fileExtension)) {
return "text/xml";
}
//預設返回型別
return "image/jpeg";
}
}
upload方法是檔案上傳的具體方法,接收File file 引數,在此基礎上可以對file進行縮放、裁剪等基礎操作後,再傳入,然後寫入雲伺服器。上傳檔案成功後,會根據Controller中自定義的成功資訊,返回成功狀態,和檔案儲存的路徑,可以把url返回前端介面展示。
Springmvc的multipart配置這個在配置檔案中定義。
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8" />
<property name="maxUploadSize" value="104857600" />
<property name="maxInMemorySize" value="4096" />
</bean>
另一種使檔案上傳,效能更高的做法是,把自己的金鑰(或者識別身份的東西)暫時授權給第三方使用者(你的網站使用者)使用者可以直接POST呼叫阿里雲的介面,即省去你自己的伺服器中轉。這樣就更加快速而且高效地實現檔案上傳的目的。
OSS物件伺服器大致的基本功能,小編就暫時分享到這裡。有關OOS的細節,還請讀者參考官方文件。文章中的不足之處,希望讀者在評論區不吝賜教。程式碼經測試有效,在整合自己專案的過程中,遇到問題,歡迎在此討論。整個專案的原始碼,小編完善後,會託管到GitHub或碼雲上,隨後共享。歡迎大家關注…