LoginSignup
0
0

More than 3 years have passed since last update.

Node.js で Azure BLOB に SAS トークン を使って、ファイルをアップロードしてダウンロードする

Posted at

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;
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0