1. 程式人生 > >OSS阿里雲端儲存——整合Spring檔案上傳,實現動靜網站資源分離

OSS阿里雲端儲存——整合Spring檔案上傳,實現動靜網站資源分離

寫在前面

在日常學習或開發java web專案的過程中,難免會遇到各種各樣的檔案上傳問題。傳統做法或固有思路,一向是把這些使用者上傳的靜態資源儲存在自己的伺服器中。這也是初學者普遍的做法。但是,長此以往,大量使用者累積的使用者靜態資原始檔,勢必會大量佔用伺服器的儲存空間,降低伺服器的效能,同時也增加了網站維護的成本。

於是,另一種做法營運而生,就是把網站的靜態資源和動態資源實現分離。增加一臺伺服器專門用來儲存靜態檔案,這不失為一種好辦法。但小編今天分享的另一種方法是在第三方雲端儲存伺服器的基礎上,實現自己的檔案上傳以及檔案雲端儲存,達到網站資源動靜分離的目的。

關於阿里雲OSS雲端儲存伺服器的具體資料和相關文件以及各個語言的API請讀者自行百度或瀏覽官網學習,這裡就不做過多的贅述。傳送門:

阿里雲官網OOS物件伺服器

以下小編主要分享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或碼雲上,隨後共享。歡迎大家關注…