Amazon S3 の署名付き URL を使ってファイルをダウンロードする Node.js コード

Amazon S3 の、既に出尽くしている話。非公開の S3 バケットでも、署名付き URL というのを作ってやれば、URL の生成から15分間限定で対象のオブジェクトをインターネット経由で普通にダウンロードできる。

サーバサイド (Node.js) のコードは次のとおり。

# バージョン 2.1295.0 で検証
$ npm install --save aws-sdk

@aws-sdk/client-s3 という別のクライアントライブラリもあるのだが、今回は aws-sdk パッケージを使用している。

const AWS = require('aws-sdk');
AWS.config.update({
  accessKeyId: '【アクセスキーID】',
  secretAccessKey: '【シークレットアクセスキー】',
  region: 'ap-northeast-1'
});

const getSignedUrl = async () => {
  const s3 = new AWS.S3();
  const params = { Bucket: '【バケット名】', Key: '【オブジェクト名】.txt' };
  
  try {
    await s3.headObject(params).promise();
  }
  catch(error) {
    if(error.code === 'NotFound') console.log('そんなファイルないよ');
    return;
  }
  
  // `params` で `Expires` (秒) プロパティを未指定だと15分間の制限となる
  try {
    const url = await new Promise((resolve, reject) => {
      s3.getSignedUrl('getObject', params, (error, url) => {
        if(error) return reject(error);
        resolve(url);
      });
    });
    console.log('The URL is :', url);
    return url;
  }
  catch(error) {
    console.log(error);
  }
};

AWS.S3headObject() でファイルの存在がチェックできる。この API は Promise 形式にも対応しているので .promise()await している。

実際に署名付き URL を発行するのは getSignedUrl() 部分。この API はコールバック形式でしか返してくれないので自前で Promise 化している。

コレで長ったらしい「署名付き URL」が生成できるので、フロントエンドでよしなにダウンロードさせてやれば良い。

// 上述のバックエンド API を呼び出して署名付き URL を取得したテイ
const response = await window.fetch('/get-signed-url');
const { url } = await response.json();

// クリックイベントを発火させてファイルをダウンロードさせる
const downloadLink = document.createElement('a');
downloadLink.href = url;  // 署名付き URL を設定する
downloadLink.download = 'example.txt';  // ファイル名
downloadLink.click();

以上。