Edited at

Azure Storage、マルチパートでアップロードできないってよ

More than 1 year has passed since last update.


概要

Azure Storage(Blob)へファイルをアップロードする際は、マルチパートが使えない模様。困った。

Blob APIを使う必要があるらしいがPUTの制限があるため、

256MB以上のファイルはブロックに分割してアップロードする必要がある模様。

しかし自分で実装するとなると、これがそこそこ面倒。困った。

そこで、ブラウザから(JavaScriptで)分割してアップロードする方法を調べていたところ、

公式のライブラリがあるようなので、実際に試してみた。

結論から言うと、ライブラリを利用することで、分割アップロードを簡単に実現することができた。

Azure Storage(Blob)へのファイルアップロードはファイルサイズに関係なく、

今回試した方法を使うで良さそう。

※ 試したコードはgithubでも公開中 azure-storage-file-uploader


デモ

azure-storage-upload-download.gif

※ 256MB以上のファイルをアップロードすると、アップロード時間が長いので1MBのファイルでのデモ


手順


Azure Storageの準備

 1. ストレージアカウントの作成

 2. CORSの設定

 3. SASトークンの生成


1. ストレージアカウントの作成

公式の手順を参考に作成するだけ

※ ストレージの種類は今回のターゲットの「BLOB ストレージ」を選択


2. CORSの設定

サイドバーから「CORS」を選択し、以下のように設定。

許可されたオリジン: *

許可されたメソッド: GET,HEAD,OPTIONS,PUT,POST
許可されたヘッダー: Accept,Content-Type,Origin,x-ms-blob-content-disposition,x-ms-blob-content-md5,x-ms-blob-content-type,x-ms-blob-type,x-ms-client-request-id,x-ms-date,x-ms-version
公開されるヘッダー: *
最大期間(秒): 0

※ 許可されたヘッダーに関しては「*」ではなく調査しがてら明示的に必要なヘッダーを指定した。


3. SASトークンの生成

サイドバーから「Shared Access Signature」を選択。

お試し程度であれば、基本デフォルト値で問題ないが、

「開始日時と有効期限の日時」のタイムゾーンは「UTC +09:00」に変更することをお忘れなく。

最後に「SASの生成」を押下し、生成された「SAS トークン」をメモっておく。

これでAzure Storageの準備は完了。


ファイルアップロードの実装

 1. ライブラリの導入

 2. 必要な情報の定数化

 3. コンテナの生成

 4. アップロード

 5. プログレス表示

 6. ダウンロード

最終的なコードはこんな感じ。ライブラリのサンプルコードを参考にしつつ進めた。

せっかくなので、アップロードだけでなく、プログレスバーやダウンロードも入れてみた。


index.html

<!DOCTYPE html>

<html>
<head>
<meta charset="utf-8">
<title>Azure Storage File Uploader</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
</head>
<body>
<div><img id="image" src=""/></div>
<input id="inputFile" type="file" />
<i id="download" class="fa fa-download" aria-hidden="true"></i>
<div><progress id="progress" max="100"></progress></div>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
html {
display: table;
}
body {
display: table-cell;
text-align: center;
vertical-align: middle;
}
#image{
width: 300px;
}
#progress {
display: none;
margin-left: -70px;
}
#download {
display: none;
cursor: pointer;
}
</style>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<script src="azure-storage.common.min.js"></script>
<script src="azure-storage.blob.min.js"></script>
<script src="main.js"></script>
</body>
</html>


main.js

const SAS_TOKEN = 'sas-token';

const CONTAINER_NAME = 'my-container-name';
const BLOB_URI = 'https://my-storage-name.blob.core.windows.net';

const blobService = AzureStorage.createBlobServiceWithSas(BLOB_URI, SAS_TOKEN);

$('#inputFile').on('change', (event) => {
blobService.createContainerIfNotExists(CONTAINER_NAME, (error, result) => {
if (error) {
console.error('create container error');
return;
}

const file = event.target.files[0];
const customBlockSize = (file.size > 1024 * 1024 * 32)? (1024 * 1024 * 4) : (1024 * 512);
blobService.singleBlobPutThresholdInBytes = customBlockSize;

const options = {
blockSize: customBlockSize,
contentSettings: {
contentDisposition: 'attachment'
}
};

beforeUpload();

let finishedOrError = false;
const speedSummary = blobService.createBlockBlobFromBrowserFile(CONTAINER_NAME, file.name, file, options, (error, result, response) => {
finishedOrError = true;
if (error) {
console.error('upload error');
return;
}
console.log('upload successfully');
afterUpload();
});

function refreshProgress() {
setTimeout(() => {
if (!finishedOrError) {
$('#progress').val(speedSummary.getCompletePercent());
refreshProgress();
}
}, 200);
}

refreshProgress();
});

});

$('#download').on('click', (event) => {
const file = $('#inputFile').prop('files').item(0);
if (file == null) return;
const downloadUrl = blobService.getUrl(CONTAINER_NAME, file.name, SAS_TOKEN);
$('#image').attr('src', downloadUrl);
});

function beforeUpload() {
$('#progress').val(0);
$('#progress').show();
$('#download').hide();
}

function afterUpload() {
$('#progress').hide();
$('#download').show();
}



1. ライブラリの導入

READMEどおりに進めればOK。

生成された以下の2ファイルをindex.htmlで読み込む。


  • azure-storage.common.min.js

  • azure-storage.blob.min.js


2. 必要な情報の定数化

以下の3つがあればOK。

SASトークン: 生成したSASトークン  ※ "?"以降の値を使用する

コンテナ名: 任意のコンテナ名

Blob URI: https:{ストレージ名}.blob.core.windows.net

const SAS_TOKEN = 'sas-token';

const CONTAINER_NAME = 'my-container-name';
const BLOB_URI = 'https://my-storage-name.blob.core.windows.net';


3. コンテナの生成

任意のコンテナ名を引数とすることで、コンテナが生成される。

既に同名のコンテナが存在する場合は、再生成されることはないので、必ず実行して問題ない。

blobService.createContainerIfNotExists(CONTAINER_NAME, (error, result) => {

ドキュメントはこちら


4. アップロード

コンテナ名、ファイル情報、オプションを指定してファイルをアップロードする。

ファイルサイズに応じたブロックに分割して、リクエストが送信される。

const file = event.target.files[0];

const customBlockSize = (file.size > 1024 * 1024 * 32)? (1024 * 1024 * 4) : (1024 * 512); // ファイルサイズに応じてブロックサイズを決定
blobService.singleBlobPutThresholdInBytes = customBlockSize;

const options = {
blockSize: customBlockSize,
contentSettings: {
contentDisposition: 'attachment'
}
};

beforeUpload();

let finishedOrError = false;

// プログレスなどが取得できる、オブジェクトが返却される
const speedSummary = blobService.createBlockBlobFromBrowserFile(CONTAINER_NAME, file.name, file, options, (error, result, response) => {
finishedOrError = true;
if (error) {
console.error('upload error');
return;
}
console.log('upload successfully');
afterUpload();
});

ドキュメントはこちら


5. プログレス表示

アップロード時に返却される、speedSummaryオブジェクトからプログレス情報を取得可能なため、

一定時間毎にプログレスバーを更新することで実現可能。

// プログレスなどが取得できる、オブジェクトが返却される

const speedSummary = blobService.createBlockBlobFromBrowserFile(CONTAINER_NAME, file.name, file, options, (error, result, response) => {
finishedOrError = true;
if (error) {
console.error('upload error');
return;
}
console.log('upload successfully');
afterUpload();
});

function refreshProgress() {
setTimeout(() => {
if (!finishedOrError) {
$('#progress').val(speedSummary.getCompletePercent());
refreshProgress();
}
}, 200);
}

refreshProgress();


6. ダウンロード

コンテナ名、ファイル名、SASトークンからダウンロードURLを生成可能。

const downloadUrl = blobService.getUrl(CONTAINER_NAME, file.name, SAS_TOKEN);

$('#image').attr('src', downloadUrl);

ドキュメントはこちら


あとがき

分割アップロードを自分で実装するとなると、そこそこ面倒なため、こういうライブラリがあるのは嬉しい。

ただ、アップロードのキャンセルができない(APIがない)のが、うーむ...という感じ。

用意されていないのは、分割してリクエストを送っている分、うまいこと制御するのが難しかったりするのかも。


参考情報