tl;dr
SAS トークンの構造を学習するために、MSのライブラリを使わないで生成しました。
公式ドキュメント : サービス SAS を作成する
コード
ファイルをアップロードして、すぐさま、ダウンロードします。
以前のコード との違いは、'permission' に 'c' を足したこと。
それ以外は同じで大丈夫みたい。
main.js
const axios = require("axios");
const SAS = require("./SAS");
const fs = require("fs");
const name = "ストレージアカウント名";
const key = "キー";
const container = "コンテナ名";
const blob = "ブロブ名";
const filename = "ファイル名";
const expiry = 1; // 1 秒間有効
const sas = new SAS(name, key);
const get = async (blob, filename) => {
// URL 生成
const url = sas.getUrl(container, blob, expiry, filename);
console.log(url);
await axios({
method: "get",
url,
responseType: "arraybuffer", // axios
}).then((result) => {
console.log(result.statusText);
const name = result.headers["content-disposition"].split("=")[1].slice(1, -1);
fs.writeFileSync(name, result.data);
});
};
const put = async (blob, filename) => {
// URL 生成
const url = sas.getUrl(container, blob, expiry, filename);
console.log(url);
const data = fs.readFileSync(filename);
await axios({
method: "put",
url,
data,
headers: {
"Content-Type": "application/octet-stream",
"x-ms-blob-type": "BlockBlob",
},
}).catch((e) => {
console.log(e);
});
};
const main = async () => {
await put(blob, filename);
await get(blob, filename + ".1");
};
main().then((result) => {
console.log(result);
});
SAS.js
const crypto = require("crypto");
class SAS {
// コンストラクタ
// @param name ストレージアカウント名
// @param key ストレージアカウントキー
constructor(name, key) {
this.account_name = name;
this.account_key = key;
}
// トークン付きURLを生成する
// @param container コンテナ名
// @param blob ブロブ名
// @param expiry_second トークン有効期間
// @param filename ファイル名
getUrl(container, blob, expiry_second, filename) {
const token = this.getToken(container, blob, expiry_second, filename);
const url = `https://${this.account_name}.blob.core.windows.net/${container}/${blob}`;
return `${url}?${token}`;
}
// トークンを生成する
// @param container コンテナ名
// @param blob ブロブ名
// @param expiry_second トークン有効期間
// @param filename ファイル名
getToken(container, blob, expiry_second, filename) {
// パラメータ
const now = new Date();
const start = ""; // this.isoDate(now, start_second);
const expiry = this.isoDate(now, expiry_second);
const version = "2018-11-09";
const resource = "b";
const permission = "rc";
const ip = ""; // "0.0.0.0/0";
const protocol = ""; // "https";
const canonicalizedResource = `/blob/${this.account_name}/${container}/${blob}`;
const rscc = "";
const rscd = `attachment; filename="${filename}"`;
const rsce = "";
const rscl = "";
const rsct = "";
const identifier = "";
const snapshot = "";
// シグネチャ (順番は厳守)
const stringToSign = [
permission,
start,
expiry,
canonicalizedResource,
identifier,
ip,
protocol,
version,
resource,
snapshot,
rscc, // Cache-Control
rscd, // Content-Disposition
rsce, // Content-Encoding
rscl, // Content-Language
rsct, // Content-Type
].join("\n");
// HMAC256
const signature = this.getHMAC256(stringToSign);
// クエリパラメータ (順番は任意)
const sas = [
["sp", permission], //
["sr", resource],
// ["st", start],
["se", expiry],
// ["sip", ip],
// ["spr", protocol],
["sv", version],
["sig", signature],
["rscd", rscd],
]
.map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
.join("&");
return sas;
}
isoDate(now, second = 0) {
const date = new Date(now.getTime() + second * 1000);
return date.toISOString().substring(0, 19) + "Z";
}
getHMAC256(input) {
// 1. キー は Buffer (Base64 で デコード)
const key = Buffer.from(this.account_key, "base64");
const hmac = crypto.createHmac("sha256", key);
// 2. 入力 は Buffer (UTF8 で デコード)
const inp = Buffer.from(input, "utf8");
// 3. 出力 は Buffer を Base64 で エンコード
const out = hmac.update(inp).digest();
return out.toString("base64");
}
// 上を簡単に書き直すと
getHMAC256_(input) {
const key = Buffer.from(this.account_key, "base64");
return crypto.createHmac("sha256", key).update(input).digest("base64");
}
}
module.exports = SAS;