Microsoft Azure Storage 實戰篇: 以Node.js使用Blob Service

Microsoft Azure上提供各式各樣的雲端服務,
其中最基本也最容易用到的應該就是Azure Storage。

這個概念就像Google Drive一樣,
我們可以在雲端儲存資料
再透過Azure提供的介面去存取或讀取這些檔案。
對公司企業來說,透過用量計價的方式,
就可以省去大筆建置Data Center的成本與人力,
輕鬆享有雲端資料儲存的服務。

Blob是Azure Storage提供的一種檔案儲存方式,
適合一般的檔案儲存,也可以建立資料夾以管理檔案,
關於Blob更詳細的介紹請參考另一篇網誌:Microsoft Azure Storage: Blob Storage 介紹
這邊要記錄如何開發使用Blob Service的程式,
其實即使是不同程式語言,
還是遵循差不多的步驟、使用差不多的API函數,
這邊因為工作上需要,我使用Node.js來開發Web App。

1. Create Azure Storage Account 建立Azure儲存體


首先我們要跟建立一個Storage Account
一個Account有一把Access Key,
就像是帳密一樣,有Key才能使用這個Storage
Account底下可以建立不同的Storage Service,享用同一把Key。

進入portal.azure.com,登入自己的azure帳號
如果是新手,帳號還沒建立的話,
請參考另一篇網誌:新手上路-在Microsoft Azure上建立Node.js應用並啟用Git發布
裡面有建立azure帳號的步驟。

選擇新增Data+Storage的service,然後新增Storage account

輸入你想要的名字之後,服務就會發布,基本上就是建立完成!

從Setting頁面可以看到Account的Access Key
這很重要,等等會用到!

2. Azure Storage SDK for Node.js


首先安裝給Node.js的azure-storage SDK
npm install azure-storage

在程式裡面,require剛安裝好的SDK,
並創立Blob Service的物件
var azure = require('azure-storage');
var blob_service =
        azure.createBlobService(storage_account_name, access_key);
傳入Storage Account的名字跟Access key,就連接到剛剛建立的Storage儲存體了!

每個blob都存放在container裡面。所以在上傳/下載blob之前,要先在你的storage裡建立container。建立的方式可以透過portal的介面,也可以透過程式:
blobService.createContainerIfNotExists(container_name,
        function (err, result, response) {
            if (err) {
                console.log("Couldn't create container %s", container_name);
                console.error(err);
            } else {
                if (result) {
                    console.log("Container %s created", container_name);
                } else {
                    console.log("Container %s already exists",
                            container_name);
                }
            }
        }
);


1) Upload 上傳

基本上傳blob的方法有三種,一個是上傳檔案,一個是上傳文字,另一個是建立writeStream上傳內容。

如果要上傳文字,使用createBlobkBlobFromText
blobService.createBlockBlobFromText(container_name, blob_name,
        'Hello, World!',
        function (error, result, response) {
            if (error){
                console.log("Couldn't upload string");
                console.error(error);
            } else {
                console.log('String uploaded successfully');
            }
        }
);

如果要上傳檔案,使用createBlobkBlobFromFile
var fileName = 'hello-world.txt';
blobService.createBlockBlobFromFile(container_name, blob_name, fileName,
        function (error, result, response) {
            if (error) {
                console.log("Couldn't upload file %s", fileName);
                console.error(error);
            } else {
                console.log("File %s uploaded successfully", fileName);
            }
        }
);
只要傳入檔名即可,不必用到fs模組。

2) Download 下載

下載blob同樣有三種方式,在這邊介紹最基本的文字與檔案。

使用getBlobToText可以將blob的內容下載成text string:
blobService.getBlobToText(container_name, blob_name,
        function (err, blobContent, blob) {
            if (err) {
                console.error("Couldn't download blob %s", blob_name);
                console.error(err);
            } else {
                console.log("Sucessfully downloaded blob %s", blob_name);
                console.log(blobContent);
            }
        }
);

callback函數裡,blobContent就是我們下載回來的文字內容,而第三個參數blob則是回傳一個blob物件,這個物件裡的欄位在後面會做介紹。

也可以將blob下載成檔案,使用getBlobToFile
var fs = require('fs');
blobService.getBlobToFile(container_name, blob_name, file_name,
        function (err, blob) {
            if (err) {
                console.error("Couldn't download blob %s", blob_name);
                console.error(err);
            } else {
                console.log("Sucessfully downloaded blob %s to %s",
                        blob_name, file_name);
                fs.readFile(file_name, function (err, fileContents) {                    if (err) {
                        console.error("Couldn't read file %s", file_name);
                        console.error(err);
                    } else {
                        console.log(fileContents);
                    }
                });
            }
        }
);

這裡callback函數同樣會回傳一個blob物件,這物件包含了你指定下載的blob的資訊,把這個物件印到console會長這樣:
BlobResult {
  container: 'test-container',
  blob: 'test-blob',
  metadata: {},
  lastModified: 'Fri, 29 Apr 2016 03:47:30 GMT',
  etag: '"0x8D36FE0FDB92B75"',
  blobType: 'BlockBlob',
  contentLength: '4',
  requestId: '6cf47af0-0001-001a-72f6-a1f5ab000000',
  contentSettings:
   { contentType: 'application/octet-stream',
     contentMD5: 'XYZabc01234DEFrst56789==' },
  lease: { status: 'unlocked', state: 'available' } }

如果想取用blob物件的值,用法就是blob.[欄位名]。比如說要知道這個blob的上次修改時間,則用blob.lastModified就可以取得數值囉!

3) List 列表

有的時候我們不需要存取blob內容,只是想知道blob存不存在,有兩個可能符合需求的函數。

第一個方法是指定blob取得他的資訊,使用getBlobProperties

blobService.getBlobProperties(container_name, blob_name,
        function (err, properties, status) {
            if (status.isSuccessful) {
                // Blob exists
            } else {
                // Blob doesn't exist
            }
        }
);


第二個方法是取得指定container裡的blob列表,使用listBlobSegmented函數:
blobService.listBlobsSegmented(container_name, null, function (err, result) {
    if (err) {
        console.log("Couldn't list blobs for container %s", container_name);
        console.error(err);
    } else {
        console.log("Successfully listed blobs for container %s", container_name);
        console.log(result.entries);
        console.log(result.continuationToken);
    }
});

其中,result.entries是一個blob array,裡面是這個container裡的所有blob物件,物件的長相就是前面文章有列出欄位的BlobResult。而result.continuationToken如果不為null的話,就表示你的blob太多了,還有下一頁的意思,可以再寫一個函數作為callback,結果還沒回傳完就recursive執行這個函數:
var blobs = [];
function aggregateBlobs(err, result, cb) {
    if (err) {
        cb(er);
    } else {
        blobs = blobs.concat(result.entries);
        if (result.continuationToken !== null) {
            blobService.listBlobsSegmented(
                    container_name, //填入自己指定的container name
                    result.continuationToken,
                    aggregateBlobs);
        } else {
            cb(null, blobs);
        }
    }
}
blobService.listBlobsSegmented(containerName, null,
        function (err, result) {
            aggregateBlobs(err, result, function (err, blobs) {
            if (err) {
                console.log("Couldn't list blobs");
                console.error(err);
            } else {
                console.log(blobs);
            }
        }
);

[Ref] http://willi.am/blog/2014/06/30/azure-blob-storage-and-node/

留言