蘑菇博客圖片服務的設計與實現

大家好,我是 陌溪

有不少小夥伴想讓我講講蘑菇中的圖片上傳功能,今天就讓我們一起來學習一下如何將圖片上傳到 七牛雲 吧~

在講如何上傳七牛雲之前,我們先來看看主流的存儲方式有哪些

第一種:直接將圖片保存到服務器的磁盤(使用 IO 流)

第二種:使用分佈式文件系統進行存儲(如 MiniIO

第三種:使用第三方的存儲服務(七牛雲

如果有體驗蘑菇博客的小夥伴,其實可以在後臺系統看到,蘑菇其實已經集成了上面的三種對象存儲方式的,默認開啓的是:本地文件存儲

本期,我們將重點講解七牛雲存儲在蘑菇中如何使用的,對於其它存儲方式,如果小夥伴感興趣的話,後面可以在寫一篇文章來講解~

什麼是對象存儲

對象存儲是面向文件的,海量的互聯網存儲,它也可以直接被稱爲:雲存儲。在對象存儲中,你不能直接打開或修改文件,但可以像 FTP 一樣上傳文件,下載文件等。

同時,對象存儲沒有像文件系統那樣有一個很多層級的文件結構,而是隻有一個 桶(bucket) 的概念,也就是 存儲空間。桶裏全部都是對象,是一種非常扁平化的存儲方式。其最大的特點就是它的對象名稱就是一個域名地址,一旦對象被設置爲 公開。互聯網所有地方都可以訪問到它,它的擁有者還可以通過 REST API 的方式訪問其中的對象。因此,對象存儲最主流的使用場景,就是存儲網站中的靜態內容,如:圖片文件視頻 等。

對象存儲結構主要由:對象對象存儲設備元數據服務器對象存儲客戶端 四部分組成

對象 是系統中數據存儲的基本單位,一個對象實際上就是文件的數據和一組屬性信息(Meta Data)的組合。每個對象是數據和數據屬性集的綜合體,數據屬性可以根據應用的需求進行設置,包括數據分佈、服務質量等。對象存儲設備 是一個智能設備,具有自己的存儲介質、處理器、內存以及網絡系統等,負責管理本地的 Object,是對象存儲系統的核心。

元數據服務器 控制 客戶端 與 對象存儲的交互,爲客戶端提供元數據,主要是文件的邏輯視圖,包括文件與目錄的組織關係、每個文件所對應的 OSD 等。主要提供以下幾個功能:對象存儲訪問、文件和目錄訪問、Client Cache 一致性。

客戶端 主要是爲了支持訪問 OSD 上的對象而設計的,現有的應用對數據的訪問大部分都是通過 POSIX 文件訪問進行的,對象存儲系統提供給用戶的也是標準的 POSIX 文件訪問接口。

第三方存儲服務

說完了對象存儲的基本原理,下面我們來聊聊第三方的存儲服務,其實也就是在對象存儲的基礎上各家產生做了一些工作,比如在雲廠商的機房搭建了對象存儲服務,並做了相關容災處理。目前市面上常見的第三方存儲服務有:

阿里雲對象存儲 OSS

騰訊雲對象存儲 COS

七牛雲對象存儲 Kodo

至於蘑菇爲啥使用的是七牛雲呢?當然是因爲想要給大家節省成本了

首先,七牛雲對象存儲每個月是會提供 10G 的標準存儲空間和 10G 免費的 HTTP CDN 流量,一般作爲個人的博客網站來說,10G 的存儲已經完全夠用了

以蘑菇博客爲例,現在一共有 1000 多篇文章,一共存儲了 14414 個圖片,也才花費了 2.21GB 的存儲空間(這當然還得靠蘑菇集成的圖片壓縮算法,把大圖都壓縮成馬賽克了。。)

除了存儲外,如果我們的圖片還要放在公網上,被大家訪問的話,那麼就還需要用到 CDN 流量了

以蘑菇爲例,一個月的 CDN 流量大概需要 9G,如果大家使用的是 HTTP 流量的話,那麼是有 10G 免費的額度,也相當於白嫖,而且還可以享受非常大的圖片 CDN 帶寬,比起存儲在自己的服務器的 1M 小水管來說,體驗感簡直拉滿了!

如何接入七牛雲存儲

下面我們來講講,如何接入七牛雲存儲,實現對圖片資源的高速訪問

按照慣例,先初始化一個 SpringBoot 項目,引入 SpringWeb 依賴後,下載即可

https://start.spring.io/

然後,將項目導入到 IDEA 中,引入七牛雲 SDK 依賴

<dependency>
    <groupId>com.qiniu</groupId>
    <artifactId>qiniu-java-sdk</artifactId>
    <version>[7.7.0, 7.7.99]</version>
</dependency>

編寫配置文件 application.yml,這裏需要配置七牛雲的公鑰、私鑰、桶名稱以及訪問的 URL,下面會講到如何獲取這幾個參數

server:
  port: 8080

qiniu:
  accessKey: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx # 公鑰
  secretKey: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # 私鑰
  bucketName: mogu-test  # 桶名稱
  path: http://riew531sd.hn-bkt.clouddn.com/  # 訪問url

# 最大文件上傳,單次最大上傳
spring:
  servlet:
    multipart:
      max-request-size: 10MB
      max-file-size: 10MB

下面,打開七牛雲官網獲取配上述置信息【點擊查看原文直達】

官網:https://www.qiniu.com/

然後,註冊賬號,完成個人認證後,即可每月獲取免費的 10G 存儲空間~

選擇對象存儲,選擇空間管理,新建空間

對象存儲:https://portal.qiniu.com/kodo/bucket

在右側填寫好存儲空間信息,這裏需要配置:存儲空間名稱(bucketName)、存儲區域,這兩個信息在上方配置中需要使用。【訪問控制,選擇私有的話,就無法直接通過 url 訪問】

空間創建完成後,會提示綁定圖片域名,如果小夥伴沒有域名的話,可以直接使用七牛雲提供的測試域名

這裏可以稍後來綁定,然後進入到對象存儲的首頁,在這裏可以看到剛剛的測試域名

然後右上方的頭像,可以看到密鑰管理

點擊後,是可以看到正在生效 accessKeysecretKey 了,這個需要保管好,最好能定期做一下切換,防止祕鑰泄露

下面,來看看七牛雲上傳圖片方法的流程圖

上傳圖片到七牛雲的代碼如下所示:

/**
 * @param file 前端傳來的圖片
 * @return 圖片的訪問路徑
 */
public String upload(MultipartFile file){
    // 生成文件名
    String fileName = getRandomImgName(file.getOriginalFilename());
    //構造一個帶指定Region對象的配置類
    // 存儲支持空間創建在不同的機房,在使用Java SDK中的UploadManager上傳文件之前,必須要構建一個上傳用的Configuration對象,在該對象中,可以指定空間對應的Region以及其他的一些影響上傳的參數。
    Configuration cfg = new Configuration();
    cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;
    //...其他參數參考類註釋
    UploadManager uploadManager = new UploadManager(cfg);

    try {
        Auth auth = Auth.create(accessKey, accessSecretKey);
        String upToken = auth.uploadToken(bucketName);
        Response response = uploadManager.put(file.getInputStream(), fileName , upToken, null, null);
        //解析上傳成功的結果
        DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
        return path + fileName;
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;

刪除文件代碼如下所示:

/**
 * 刪除七牛雲文件
 *
 * @return
 */
public String deleteFile(String fileName) {
    //構造一個帶指定Zone對象的配置類
    Configuration cfg = new Configuration();
    //獲取上傳憑證
    Auth auth = Auth.create(accessKey, accessSecretKey);
    BucketManager bucketManager = new BucketManager(auth, cfg);
    try {
        bucketManager.delete(bucketName, fileName);
        return "刪除成功";
    } catch (QiniuException ex) {
        //如果遇到異常,說明刪除失敗
        ex.printStackTrace();
        return "刪除失敗";
    }
}

完整的 QiNiuUtil.java 如下:

@Component
public class QiNiuUtil {

    @Value("${qiniu.accessKey}")
    private  String accessKey;      //公鑰
    @Value("${qiniu.secretKey}")
    private  String accessSecretKey;   //私鑰
    @Value("${qiniu.bucketName}")
    private  String bucketName;   // 存儲空間
    @Value("${qiniu.path}")
    private  String path;       // 域名

/**
 * @param file 前端傳來的圖片
 * @return 圖片的訪問路徑
 */
public String upload(MultipartFile file){
    // 生成文件名
    String fileName = getRandomImgName(file.getOriginalFilename());
    //構造一個帶指定Region對象的配置類
    // 存儲支持空間創建在不同的機房,在使用Java SDK中的UploadManager上傳文件之前,必須要構建一個上傳用的Configuration對象,在該對象中,可以指定空間對應的Region以及其他的一些影響上傳的參數。
    Configuration cfg = new Configuration();
    cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;
    //...其他參數參考類註釋
    UploadManager uploadManager = new UploadManager(cfg);

    try {
        Auth auth = Auth.create(accessKey, accessSecretKey);
        String upToken = auth.uploadToken(bucketName);
        Response response = uploadManager.put(file.getInputStream(), fileName , upToken, null, null);
        //解析上傳成功的結果
        DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
        return path + fileName;
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

    /**
     * 刪除七牛雲文件
     *
     * @return
     */
    public String deleteFile(String fileName) {
        //構造一個對象的配置類
        Configuration cfg = new Configuration();
        //獲取上傳憑證
        Auth auth = Auth.create(accessKey, accessSecretKey);
        BucketManager bucketManager = new BucketManager(auth, cfg);
        try {
            bucketManager.delete(bucketName, fileName);
            return "刪除成功";
        } catch (QiniuException ex) {
            //如果遇到異常,說明刪除失敗
            ex.printStackTrace();
            return "刪除失敗";
        }
    }

    /**
     * @Description: 生成唯一圖片名稱
     * @Param: fileName
     * @return: 雲服務器fileName
     */
    public static String getRandomImgName(String fileName) {
        int index = fileName.lastIndexOf(".");

        if (fileName.isEmpty() || index == -1){
            throw new IllegalArgumentException();
        }
        // 獲取文件後綴
        String suffix = fileName.substring(index).toLowerCase();
        // 生成UUID
        String uuid = UUID.randomUUID().toString().replaceAll("-""");
        // 拼接新的名稱
        return uuid + suffix;
    }
}

最後,在編寫 Controller ,提供 http 接口供外界調用

@RestController
public class UploadController {

    @Autowired
    private QiNiuUtil qiNiuUtil;

    @PostMapping("/upload")
    public String upload(@RequestBody MultipartFile file) {
        return qiNiuUtil.upload(file);
    }

    @PostMapping("/delete")
    public String upload(@RequestParam String fileName) {
        return qiNiuUtil.deleteFile(fileName);
    }
}

完整的代碼壓縮包,陌溪也已經上傳到網盤,在公衆號回覆【qiniu】即可下載

測試七牛雲

完成代表編寫後,啓動項目開始進行圖片上傳測試

首先,測試一下上傳功能,選擇對應的文件點擊上傳,上傳成功後即可返回圖片 url 地址

打開七牛雲的文件管理目錄,即可查看到剛剛上傳的文件:

調用 delete 方法,輸入剛剛的文件名,即可刪除剛剛上傳的文件

再次到七牛雲後臺管理中,可以看到剛剛刪除的配置

蘑菇集成七牛雲配置

在蘑菇社區中,集成了 七牛雲、阿里雲、Minio、本地存儲四種方式,通過配置表保存各個雲存儲的配置信息

可以通過切換 TAB ,來配置不同的存儲模式,來滿足大家對於不同存儲方式的需求。

下面,是蘑菇圖片服務的完整流程圖

如果想了解關於蘑菇圖片存儲的更多細節,可以下載蘑菇源碼體驗吧~

Gitee:https://gitee.com/moxi159753/mogu_blog_v2

好了,本期的蘑菇第三方存儲學習就到這裏了

我是陌溪,我們下期再見。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/QO-te-Wy2M7m-fWxfBRfkA