0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

S3とファイルを共通に扱う

Last updated at Posted at 2022-06-26

タイトルにある通り、S3を扱うNode.jsライブラリです。
すぐ忘れるので、備忘録として残しておきました。
おそらくもっと良いやり方があるかもしれず、いろんなケースに対応できているわけではないように思いますが、各所でカスタマイズするためのベースにはなるかなあと思います。

ファイルとS3どちらも共通に扱えるようにしています。
また、AWS S3の代わりにMinioでも利用できます。

storage.js
'use strict';

const fs = require('fs').promises;
var AWS = require('aws-sdk');
AWS.config.update({
  // region: "ap-northeast-1",
  // minio用
  region: "[Minioのリージョン名]",
});
var s3  = new AWS.S3({
  // minio用
  accessKeyId: '[MinioのアクセスキーID]' ,
  secretAccessKey: '[Minioのシークレットアクセスキー]',
  endpoint: 'http://[Minioのサーバ]:9000',
  s3ForcePathStyle: true, // needed with minio?
  signatureVersion: 'v4'
});

class Storage{
  constructor(folder, mode = "file"){
    this.folder = folder;
    this.mode = mode;
  }

  async createFolder(){
    if( this.mode == "s3" ){
      // S3用
      return this.s3_createFolder();
    }else
    if( this.mode == "file" ){
      // ファイル用
      return this.file_createFolder();
    }
  }

  async s3_createFolder(){
    var params = {
      Bucket: this.folder, 
    };
    return s3.createBucket(params).promise();
  }

  async file_createFolder(){
    return fs.mkdir(this.folder);
  }

  async deleteFolder(){
    if( this.mode == "s3" ){
      // S3用
      return this.s3_deleteFolder();
    }else
    if( this.mode == "file" ){
      // ファイル用
      return this.file_deleteFolder();
    }
  }

  async s3_deleteFolder(){
    await this.deleteAllObjects();
    var params = {
      Bucket: this.folder
    };
    return s3.deleteBucket(params).promise();
  }

  async file_deleteFolder(){
    await this.deleteAllObjects();
    return fs.rmdir(this.folder);
  }

  async deleteObject(fname){
    if( this.mode == "s3" ){
      // S3用
      return this.s3_deleteObject(fname);
    }else
    if( this.mode == "file" ){
      // ファイル用
      return this.file_deleteObject(fname);
    }
  }

  async s3_deleteObject(fname){
    var param_delete = {
      Bucket: this.folder,
      Key: fname
    };
    return s3.deleteObject(param_delete).promise();
  }

  async file_deleteObject(fname){
    return fs.unlink(this.folder + "/" + fname);
  }

  async deleteAllObjects(){
    var list = await this.listObjects();
    for( var i = 0 ; i < list.length ; i++ )
      await this.deleteObject(list[i]);
  }

  async listObjects(){
    if( this.mode == "s3" ){
      // S3用
      return this.s3_listObjects();
    }else
    if( this.mode == "file" ){
      // ファイル用
      return this.file_listObjects();
    }
  }

  async s3_listObjects(){
    var params = {
      Bucket: this.folder,
    };
    var list = [];
    var result;
    do{
      result = await s3.listObjects(params).promise();
      if( result.Contents )
        for( var i = 0 ; i < result.Contents.length ; i++ )
          list.push(result.Contents[i].Key);

      if( result.NextContinuationToken )
        params.ContinuationToken = result.NextContinuationToken;
    }while(result.NextContinuationToken);

    return list;
  }

  async file_listObjects(){
    return fs.readdir(this.folder);
  }

  async loadBinary(fname){
    if( this.mode == "s3" ){
      // S3用
      return this.s3_loadBinary(fname);
    }else
    if( this.mode == "file" ){
      // ファイル用
      return this.file_loadBinary(fname);
    }
  }

  async s3_loadBinary(fname){
    var param_get = {
      Bucket: this.folder,
      Key: fname
    };
    var obj = await s3.getObject(param_get).promise();
    return obj.Body;
  }

  async file_loadBinary(fname){
    return fs.readFile(this.folder + "/" + fname);
  }

  async loadBinaryPartial(fname, offset, length){
    if( this.mode == "s3" ){
      // S3用
      return this.s3_loadBinaryPartial(fname, offset, length);
    }else
    if( this.mode == "file" ){
      // ファイル用
      return this.file_loadBinaryPartial(fname, offset, length);
    }
  }

  async s3_loadBinaryPartial(fname, offset, length){
    var param_get = {
      Bucket: this.folder,
      Key: fname,
      Range: "bytes=" + offset + "-" + (offset + length - 1)
    };
    var obj = await s3.getObject(param_get).promise();
    return obj.Body;
  }

  async file_loadBinaryPartial(fname, offset, length){
    var stat = await fs.lstat(this.folder + "/" + fname);
    if( stat.size < (offset + length) )
      throw "file size short";

    var buffer = new Uint8Array(length);

    var fp = await fs.open(this.folder + "/" + fname);
    var buffer = await fp.read(buffer, 0, length, offset);
    fp.close();
    return buffer.buffer;
  }

  async putBinary(fname, body){
    if( this.mode == "s3" ){
      // S3用
      return this.s3_putBinary(fname, body);
    }else
    if( this.mode == "file" ){
      // ファイル用
      return this.file_putBinary(fname, body);
    }
  }

  async s3_putBinary(fname, body){
    var param_put = {
      Bucket: this.folder,
      Key: fname,
      Body: body
    };

    return s3.putObject(param_put).promise();
  }

  async file_putBinary(fname, body){
    return fs.writeFile(this.folder + "/" + fname, body);
  }

  async isExist(fname){
    if( this.mode == "s3" ){
      // S3用
      return this.s3_isExist(fname);
    }else
    if( this.mode == "file" ){
      // ファイル用
      return this.file_isExist(fname);
    }
  }

  async s3_isExist(fname){
    try{
      var buffer = await this.loadBinaryPartial(fname, 0, 1);
      return true;
    }catch(error){
      if( error.code == "InvalidRange" )
        return true;
      else
        return false;
    }
  }

  async file_isExist(fname){
    try{
      var stat = await fs.lstat(this.folder + "/" + fname);
      return true;
    }catch(error){
      return false;
    }
  }
}

module.exports = Storage;

その他

S3互換のMinioについては以下が参考になります。
 SwaggerでLambdaのデバッグ環境を作る(7):AWS S3トリガをデバッグする

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?