『有能執事くん』、デスクトップアプリに進化しました!
こんにちは!
ALH の開発エンジニア、REIYAです。
以前つくった『有能執事くん バッチ版』
noteでも紹介の記事を書き、ソースも公開していました。
今回はなんとそのデスクトップアプリ版をつくってみましたので、ご紹介します。
前回の記事はこちら↓↓
執事くん
以前のバッチ版を、デスクトップアプリ版にしたもの。
Windows用です。
Electronにて実装しました。頒布はここクリックでDL
zip展開後にREADME.mdがあるので、したがってインストールバッチを実行してください。
起動すると、ランダムなローディングアニメーションが流れたのち、以下のホーム画面が表示されます。
バッチ版との差異
バッチ版の弱点として、フォルダを自動で作成し、構成を縛る、というものがありました。
こちらのアプリでは設定機能にて、本アプリで使用するフォルダを指定することができます。
実装機能
となります。ボタンホバーで説明文が表示されるのでチュートリアルは不要でしょう。
バッチ版と違い、追加バッチを呼び出す機能はありません。
そこまでこの機能が活躍することがないと判断し、今回のアプリでは淘汰されました。。。
また、バックアップ進捗バーや圧縮アニメなど、視覚的な機能の向上がメインとなっているうえ
大きなファイルを圧縮する際には、内臓した7zipを用いた圧縮を使用します。
ノンビルドでzipを配布しているのでソースは見放題です。ご自由にカスタムできます。
Electron以外の実装部分ソースの一部を以下で紹介したいと思います。
ソース
全量はこちら
バックアップ機能部分のソースはこんな感じです。
// ========================================
// BK機能用画面をロード
// ========================================
import Forge from '../../core/Forge.js'
import CMD from '../../core/CMD.js'
import Fsys from '../../core/Fsys.js'
// exeファイルのあるディレクトリを取得
const chdir = CMD.cmdSync('chdir').replace(/\r\n/g, '');
// config.json
const cfDict = JSON.parse(
Fsys.read(`${chdir}\\src\\config.json`)
.replace(/\r\n/g, ''));
// タイトルを設定
const setTitle = v => document.querySelector('#title').innerHTML = v;
// 進捗バー
let progress = document.querySelector('.progress-bar');
// 作業フォルダをハンドリング
function handl() {
setTitle('ハンドリング開始');
if (!Fsys.existance(cfDict['bkto'])
|| !Fsys.existance(cfDict['bkfrom'])) {
Forge.load('contents', 'home', () => {
document.querySelector('#console').innerHTML = 'BKフォルダが存在しません, 設定画面から設定してください。';
});
return false;
}
return true;
}
let bktoDirName = null;
let bkFileAllNo = null;
// 実行関数
function exeBk() {
setTitle('処理開始');
let now = new Date();
let yyyy = now.getFullYear();
let mm = now.getMonth() >= 9 ? now.getMonth() + 1 : `0${now.getMonth() + 1}`;
let dd = now.getDate();
// 新規ヘッダ or 今日と同日でない場合
let dirname = `${cfDict['bkheader']}_${yyyy}_${mm}_${dd}_ver1`;
let flist = Fsys.fList(cfDict['bkto']);
let dirList = Fsys.dirList(cfDict['bkto']);
if (flist || dirList) {
// ファイル名も考慮
// 同ヘッダファイルについて
let sameHeadFile = flist
? flist.concat(dirList).filter(v => v)
.filter(v => v.startsWith(cfDict['bkheader'])).sort()
: dirList.concat(flist).filter(v => v)
.filter(v => v.startsWith(cfDict['bkheader'])).sort();
if (sameHeadFile.length != 0) {
// 最新バージョンを取得
let el = /(.+)_(.+)_(.+)_(.+)_ver(.+)/.exec(sameHeadFile.slice(-1)[0]);
// 今日と同日なら, バージョンを+1
if (el[2] == yyyy && el[3] == mm && el[4] == dd) {
// ファイルの場合拡張子を削除
let ver = el[5].split('.')[0];
dirname = `${cfDict['bkheader']}_${yyyy}_${mm}_${dd}_ver${Number(ver) + 1}`;
}
}
}
setTitle('名前ok');
bktoDirName = `${cfDict['bkto']}\\${dirname}`;
new Promise(resolve => {
// フォルダ作成
setTitle('フォルダ作成を開始');
let reMkdir = Fsys.mkdir(bktoDirName);
setTitle('フォルダ作成完了' + reMkdir);
resolve();
})
.then(() => {
// 並列でコピー、進捗を監視して圧縮/転送
setTitle('コピー開始');
let resRobocp = Fsys.robocopy(cfDict['bkfrom'], bktoDirName);
setTitle('robocopyコマンド 結果' + resRobocp);
let asdasd = Fsys.count(cfDict['bkfrom']);
setTitle('カウント 結果' + asdasd);
calcProgress(Fsys.count(cfDict['bkfrom']));
});
}
// 進捗監視, 圧縮, 転送
function calcProgress(bkAll) {
if (!bkAll) {
calcProgress(Fsys.count(cfDict['bkfrom']));
return;
}
let transfared = 0;
try {
transfared = Number(Fsys.count(bktoDirName));
} catch(e) {
// 0件
}
setTitle(`コピー中(${transfared}/${bkAll})`);
progress.style.width = `${Math.floor(transfared/bkAll*100)}%`;// 進捗バー更新
if (transfared >= bkAll) {
// コピー完了
new Promise(resolve => {
progress.style.width = `100%`;
setTitle(`コピー完了`);
setTimeout(() => resolve(), 300);
})
// 圧縮画面表示
.then(() => new Promise(resolve => {
setTitle('圧縮中...');
document.querySelector('.progress-base').style.display = 'none';
document.querySelector('.bg').classList.add('compress');
setTimeout(() => resolve(), 2000);
}))
// 圧縮/コピーフォルダ削除
.then(() => new Promise(resolve => {
let resComp = Fsys.compress(bktoDirName);
if (!resComp) {
setTitle('圧縮失敗 7zipでリトライ');
let resSeven = CMD.cmdSync(`${chdir}\\src\\lib\\7za.exe a ${bktoDirName}.zip ${bktoDirName}`);
setTitle(resSeven);
}
let resDel = Fsys.delCmd(bktoDirName);
if (!resDel) {
setTitle('BK用にコピーしたファイルを削除できませんでした');
}
// いずれかに失敗した場合、ここで終了
if (!resComp || !resDel) {
return;
}
if (resComp && resDel) {
setTitle('圧縮完了');
}
resolve();
}))
// 転送しない場合は終了
.then(() => new Promise(resolve => {
if (!Fsys.existance(cfDict['sendto'])) {
setTimeout(() => {
Forge.load('contents', 'home');
}, 1000);
return;
}
resolve();
}))
// 転送画面表示
.then(() => new Promise(resolve => {
setTitle('転送中...');
document.querySelector('.bg').classList.remove('compress');
document.querySelector('.bg').classList.add('transfar');
setTimeout(() => resolve(), 1000);
}))
// 転送
.then(() => new Promise(resolve => {
Fsys.cpSync(`${bktoDirName}.zip`, cfDict['sendto']);
setTitle('転送完了');
setTimeout(() => {
document.querySelector('.bg').classList.remove('transfar')
Forge.load('contents', 'home');
resolve();
}, 1000);
}));
return;
}
// コピーが完了するまで再帰
setTimeout(() => calcProgress(bkAll), 100);
}
// ハンドリングして実行
setTitle('処理を開始します');
if (handl()) {
exeBk();
}
ALH Inc.について知る
↓ ↓ ↓ 採用サイトはこちら ↓ ↓ ↓
↓ ↓ ↓ ALHについてはこちら ↓ ↓ ↓
↓ ↓ ↓ もっとALHについて知りたい? ↓ ↓ ↓