概要
あるフォルダに不定期にCSVファイルが作成されるので、そのCSVファイルをS3にアップロードするプログラムをNode.jsで作成します。
処理に失敗するとAWS SESを使って、管理者へメールを配信します。
loggerも使って何が起こったかを追えるようにします。
全コードはこちら
フロー
起動時に監視フォルダとローカルの保存フォルダが存在するかチェック
initial_check.js
constfs=require("fs");constfsPromises=fs.promises;constinitialCheck=async(WATCHING_DIR,DEST_DIR)=>{try{awaitfsPromises.access(WATCHING_DIR,fs.constants.R_OK|fs.constants.W_OK);awaitfsPromises.access(DEST_DIR,fs.constants.R_OK|fs.constants.W_OK);}catch(error){thrownewError("監視/管理フォルダへアクセスできない");}};module.exports=initialCheck;
指定のフォルダが存在しなければ、プログラムは終了。その際にAWS SESから管理者へメールが配信されるようにしてあります。
chokidarでwatcherを起動して正常動作をチェック
watcherの設定
watcher.js
constchokidar=require("chokidar");// フォルダ監視用constWATCHING_DIR=require("./config.json").WATCHING_DIR;// Initialize watcher.constwatcher=chokidar.watch(WATCHING_DIR,{ignored:/[\/\\]\./,persistent:true,usePolling:true,interval:10000});module.exports=watcher;
watcherの動作確認
monitoring.js
constwatcher=require("./watcher");// フォルダ監視用watcher.on("ready",async()=>{awaitlogger.info("Initial scan complete. Ready for changes");constwatchedPaths=watcher.getWatched();awaitlogger.info("watchedPaths :",watchedPaths);});
監視フォルダにファイルが追加されたときの動作を設定
monitoring.js
// ファイルの追加を検知watcher.on("add",asyncfilePath=>{awaitlogger.info("add file: ",filePath);try{awaitbucketExistCheck();awaitlogger.info("Bucket Existed.");awaitfileCopyUploadDelete(filePath);}catch(error){awaiterrorState(error);}});
S3のバケットが存在することを確認してから追加されたファイルをS3へアップロードします。
watcherがエラーとなった場合
monitoring.js
watcher.on("error",asyncerror=>{awaiterrorState(error);awaitwatcher.close().then(()=>logger.info("Watcher closed: watcher on error"));});
S3バケットが存在するかをチェック
monitoring.js
constbucketExistCheck=async()=>{try{awaits3.headBucket({Bucket:BUCKET}).promise();}catch(error){thrownewError("S3 Bucket not exist!");}};
追加されたファイルを処理する流れ
- ローカルで保存するためフォルダに
YYYYMM
の名前でフォルダを作成 - そのフォルダに追加されたファイルをコピー
- 追加されたファイルをS3へアップロードするためのパラメーターを作成
- S3へアップロード
- 監視フォルダから追加されたファイルを削除
monitoring.js
constfileCopyUploadDelete=asyncfilePath=>{constfilenameParse=path.parse(filePath);// copyする前にYYYYMMのフォルダが存在するか確認して、存在しなければフォルダ作成するconstsendPathThisMonthDir=awaitmkdirThisMonth();constdestFilePath=path.join(sendPathThisMonthDir,filenameParse.base);awaitfsPromises.copyFile(filePath,destFilePath,COPYFILE_EXCL);awaitlogger.info("File copy success!",filePath,destFilePath);// コピー失敗した場合は? => 'EEXIST: file already exists, copyfile'のエラーメッセージthrow// COPYFILE_EXCLを指定しているためコピー先に同名ファイルがあった場合は上記エラー// renameメソッドでもファイル移動が可能だが、移動先に同名ファイルが存在する場合に上書きとなってしまうため利用せずconstuploadParams=awaitcreateUploadParams(filePath);awaitlogger.info("File Read Success",filePath);constdata=awaits3.putObject(uploadParams).promise();awaitlogger.info("Upload Success",data);awaitfsPromises.unlink(filePath);awaitlogger.info("File delete Success!",filePath);};
YYYYMM
のフォルダが存在しなければ作成する↓
monitoring.js
constmkdirThisMonth=async()=>{try{consttoday=newDate();constmonthMM=("0"+(today.getMonth()+1)).slice(-2);constyyyymm=today.getFullYear().toString()+monthMM;constdirPath=path.join(DEST_DIR,yyyymm);awaitfsPromises.mkdir(dirPath);logger.info("Make Directory",dirPath);returndirPath;}catch(error){if(error.code==="EEXIST"){logger.warn(error.message);returnerror.path;}thrownewError(error);}};
S3へアップロードするためのパラメーターを作成↓
ファイルはcsvからjsonへ変換。なんとなくファイル名にランダム文字列を追加。ContentMD5
キーを指定して正しくアップロードされたかをチェック
monitoring.js
constcreateUploadParams=asyncfilePath=>{try{constbody=awaitorderCsvToJson(filePath);// ファイルの中身をjson形式へ変換constmd5hash=crypto.createHash("md5");constmd5sum=md5hash.update(body).digest("base64");constrandomString=crypto.randomBytes(8).toString("hex");// ファイル名が重複しないようにするconstfilenameParse=path.parse(filePath);constkey=filenameParse.name+"_"+randomString+".json";return{Bucket:BUCKET,Key:key,Body:body,ContentMD5:md5sum};}catch(error){thrownewError(error);}};
エラーが発生した場合はSESでメール配信
monitoring.js
consterrorState=asyncerror=>{awaitlogger.error(error.message);console.error(error);// 管理者へメール配信constsesParams=createSESParams(error);awaitsesSendMail(sesParams).then(res=>{logger.info("管理者へメール配信",res);}).catch(error=>{logger.error("メール配信エラー",error);});};
最後に
おおまかに書きました。
詳細はGitHubをご確認ください。
もっと簡単な方法があるのだと思います。