概要
Azure Storage(Blob)へファイルをアップロードする際は、マルチパートが使えない模様。困った。
Blob APIを使う必要があるらしいがPUTの制限があるため、
256MB以上のファイルはブロックに分割してアップロードする必要がある模様。
しかし自分で実装するとなると、これがそこそこ面倒。困った。
そこで、ブラウザから(JavaScriptで)分割してアップロードする方法を調べていたところ、
公式のライブラリがあるようなので、実際に試してみた。
結論から言うと、ライブラリを利用することで、分割アップロードを簡単に実現することができた。
Azure Storage(Blob)へのファイルアップロードはファイルサイズに関係なく、
今回試した方法を使うで良さそう。
※ 試したコードはgithubでも公開中 azure-storage-file-uploader
デモ
※ 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. ダウンロード
最終的なコードはこんな感じ。ライブラリのサンプルコードを参考にしつつ進めた。
せっかくなので、アップロードだけでなく、プログレスバーやダウンロードも入れてみた。
<!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>
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がない)のが、うーむ...という感じ。
用意されていないのは、分割してリクエストを送っている分、うまいこと制御するのが難しかったりするのかも。