見出し画像

AzureのBlobストレージに保存しているファイルをZIPに圧縮してダウンロードするには?

Azureでとあるサービスを開発中です。

Blobストレージにファイルを保存しておき、一括でダウンロードする仕組みを開発しようとしたところ、思いのほか苦戦しました(^^ゞ

忘れないように&きっと他にも困っている人はいるはずと言うことで残しておきます。

前提条件

環境: Node.js(v12)

使用ライブラリ: @azure/storage-blob、archiver

アプローチ

archiverで圧縮しながら、Blobストレージに保存できると、ファイルを保存できない環境(例えばAzure Functionsなど)でも活用できますので、スマートです。

このような実装は以下のアプローチで実現できます。

画像1

・BlockBlobClientのdownloadでファイルデータを取得
・取得したファイルデータのReadストリーム(BlobDownloadResponseModel.readableStreamBody)を、archiverのappendで圧縮
・WriteストリームとReadストリームを繋ぐために、Transformストリームを準備
・ZIP圧縮のデータはarchiverのpipeにTransformストリームを指定
・ZIP圧縮のデータのBlobストレージへの保存はBlockBlobClientのuploadStreamにTransformストリームを指定

ポイントは、ReadとWriteができるストリームが必要になりますので、Transfromストリームを利用する点です。

実装

archiverのpipeで出力したWriteストリームを、BlockBlobClientのuploadStreamで入力として使うReadストリームに変換するためのTransformストリームクラスを作成します。

/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Transform } from 'stream';

export class ForwardingStream extends Transform {
   _transform (chunk, encoding, callback) {
       callback (null, chunk);
   }
}

Blobストレージから取得、archiverで圧縮、ZIPをBlobストレージに保存する実装です。

const blobServiceClient: BlobServiceClient = BlobServiceClient.fromConnectionString('<<Azure Storage接続文字列>>');
const containerName = '<<コンテナ名>>';
const containerClient: ContainerClient = blobServiceClient.getContainerClient(containerName);

const zipBlobName = '<<ZIPファイル名>>.zip';
const zipBlockBlobClient: BlockBlobClient = containerClient.getBlockBlobClient(zipBlobName);

const stream = new ForwardingStream();

const archive = archiver('zip', {
   zlib: { level: 9 }
});

archive.on('error', (err) => {
   throw err;
});

archive.pipe(stream);

const blockBlobClient: BlockBlobClient = containerClient.getBlockBlobClient('<<圧縮するBlobファイル名>>');
const downloadBlockBlobResponse: BlobDownloadResponseModel = await blockBlobClient.download(0);
archive.append(downloadBlockBlobResponse.readableStreamBody, { name: '<<圧縮するBlobファイル名>>' });

const uploadPromise = zipBlockBlobClient.uploadStream(stream);
await archive.finalize();
await uploadPromise;

これで、Blobストレージに保存されているファイルをZIP圧縮して、Blobストレージに保存できます。

ZIP圧縮したBlobファイルをダウンロードするには、SASを使って一時的にダウンロードできるURLを作成し、それを使うのが良さそうです。

サイズが大きいファイルでもダウンロードできますね。

SASを使って、BlobファイルをダウンロードするURLを作成するには以下のように実装します。

const storageSharedKeyCredential = new StorageSharedKeyCredential('<<Account Name>>', '<<Account Key>>');
const permissions = BlobSASPermissions.parse('r');
const startDate = new Date();
const expireDate = new Date(startDate);
expireDate.setMinutes(startDate.getMinutes() + 60);
startDate.setMinutes(startDate.getMinutes() - 1);

const queryParams = generateBlobSASQueryParameters(
   {
       containerName: '<<コンテナ名>>',
       blobName: '<<Blobファイル名>>',
       permissions: permissions,
       startsOn: startDate,
       expiresOn: expireDate,
       protocol: SASProtocol.Https,
   },
   storageSharedKeyCredential
);

return `${<<Azure Storage URL>>}${<<コンテナ名>>}/${<<コンテナ名>>}?${queryParams.toString()}`;

いいなと思ったら応援しよう!