タイトルにある通り、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トリガをデバッグする