node.jsからAmazonS3へファイルアップロード

# 確認した環境
node.js: 10.17.0
s3-client: 4.4.2

# s3-clientのインストール
s3-clientをインストールします。

```
npm i s3-client
```

# s3クライアントのインスタンスを作成

```Javascript
import s3 from "s3-client";

const client = s3.createClient({
  s3Options: {
    accessKeyId: ここにS3のアクセスキーID,
    secretAccessKey: ここにs3のシークレットキー,
  }  
})
```

# s3へアップロード
s3-clientのreadmeによるアップロードのサンプルは以下です。

```Javascript
var params = {
  localFile: "some/local/file",
 
  s3Params: {
    Bucket: "s3 bucket name",
    Key: "some/remote/file",
    // other options supported by putObject, except Body and ContentLength.
    // See: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#putObject-property
  },
};
var uploader = client.uploadFile(params);
uploader.on('error', function(err) {
  console.error("unable to upload:", err.stack);
});
uploader.on('progress', function() {
  console.log("progress", uploader.progressMd5Amount,
            uploader.progressAmount, uploader.progressTotal);
});
uploader.on('end', function() {
  console.log("done uploading");
});
```

サンプルのままだと使いづらいんで、Promiseオブジェクトでラップし、async/awaitが使えるようにします。


```Javascript
const uploadFileToS3 = (bucketName, remoteDir, localFilePath) => {
  return new Promise(async (resolve, reject) => {
    const fileName = localFilePath.split('/').pop()
    const params = {
      localFile: localFilePath,
      s3Params: {
        Bucket: bucketName,
        Key: remoteDir + '/' + fileName  // S3はオブジェクトストレージなのでフォルダの概念はないが、オブジェクトキーにスラッシュ区切りのパス名をつけることで、それをフォルダと解釈する
      },
    };
    const uploader = await client.uploadFile(params);
    uploader.on('error', (err) => {
      reject(err.stack)
    });
    uploader.on('end', function () {
      resolve()
    });
  });
}
```

これで、async/awaitを使えるようになりました。

```Javascript
(async () => {
  await uploadFileToS3(s3のbacket名, s3のフォルダ名, ローカルのファイルパス)
})();
```

フォルダをまるごとアップロードしたい場合は、以下を使用します。

```Javascript
const uploadS3Dir = async (bucketName, localDir, remoteDir = null) => {
  return new Promise(async (resolve, reject) => {
    var params = {
      localDir,
      deleteRemoved: false, // default false, whether to remove s3 objects
      // that have no corresponding local file.

      s3Params: {
        Bucket: bucketName,
        Prefix: remoteDir,
        // See: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#putObject-property
      },
    };
    var uploader = client.uploadDir(params);
    uploader.on('error', err => {
      reject(err.stack)
    });
    uploader.on('end', () => {
      resolve()
    });
  });
}
```

# s3のファイルリスト取得

S3バケット名を指定し、そこにある全てのファイルリストを再帰的に取得します。


```Javascript
const listS3Objects = async (bucketName, remoteDir = null) => {
  return new Promise(async (resolve, reject) => {
    var params = {
      recursive: true,  // これをfalseにすると１階層のみの取得となる
      s3Params: {
        Bucket: bucketName,
        Prefix: remoteDir,
        // See: https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#listObjects-property
      },
    };
    const finder = client.listObjects(params);
    let objects = []
    finder.on('data', function (data) {
      objects = data
    });
    finder.on('error', err => {
      reject(err.stack)
    });
    finder.on('end', () => {
      resolve(objects)
    });
  });

}
```

# s3にあるファイルの削除
S3バケット名と、オブジェクトキー文字列の配列を指定し、ファイルを削除します。


```Javascript
// objectKeys: [ remoteFile1, remoteFile2, ...]
const deleteS3Objects = async (bucketName, objectKeys) => {
  return new Promise(async (resolve, reject) => {
    var params = {
      Bucket: bucketName,
      Delete: {
        Objects: objectKeys.map(k => ({ Key: k }))
      }
    };
    const deleter = client.deleteObjects(params);
    deleter.on('error', err => {
      reject(err.stack)
    });
    deleter.on('end', () => {
      resolve()
    });
  });
}
```

# サンプル
S3のバケットをいったん空にしてファイルをアップロードするサンプルです。

```Javascript
// ここに上記の各関数を記述

(async () => {
  const configs = {
    acckey: S3のアクセスキーID,
    seckey: S3のシークレットキー,
    bucket: S3のバケット名,
  }

  const client = s3.createClient({
    s3Options: {
      accessKeyId: configs.acckey,
      secretAccessKey: configs.seckey,
    }
  })

  // 既存のobjectを削除
  const list = await listS3Objects(configs.bucket)
  const keys = list.Contents.map(i => i.Key)
  //console.log(keys)
  if (keys.length > 0) await deleteS3Objects(configs.bucket, keys)
  
  await uploadFileToS3(configs.bucket, 'foo', './bar.txt')  // このプログラムの実行フォルダ直下に、`bar.txt`ファイルが存在する前提

})();

```