import get from 'lodash-es/get.js'
import filter from 'lodash-es/filter.js'
import isfun from 'wsemi/src/isfun.mjs'
import getErrorMessage from 'wsemi/src/getErrorMessage.mjs'
import fsCleanFolder from 'wsemi/src/fsCleanFolder.mjs'
import fsTreeFolder from 'wsemi/src/fsTreeFolder.mjs'
import WFtp from 'w-ftp/src/WFtp.mjs'
/**
* 由FTP或SFTP下載檔案至本機暫存資料夾fdDwStorageTemp,並回傳所下載之檔案清單
*
* 下載前會先清空fdDwStorageTemp,僅下載st.fdIni內第1層之檔案,且強制複寫本機數據
*
* @param {Object} st 輸入設定FTP連線資訊物件
* @param {String} [st.transportation='FTP'] 輸入傳輸協定字串,可選'FTP'、'SFTP',預設'FTP'
* @param {String} [st.hostname=''] 輸入hostname字串,預設''
* @param {Integer} [st.port=21|22] 輸入port正整數,當transportation='FTP'預設21,當transportation='SFTP'預設22
* @param {String} [st.username=''] 輸入帳號字串,預設''
* @param {String} [st.password=''] 輸入密碼字串,預設''
* @param {String} [st.fdIni='./'] 輸入同步資料夾字串,預設'./'
* @param {String} fdDwStorageTemp 輸入最新下載檔案存放資料夾字串
* @param {Object} [opt={}] 輸入設定物件,預設{}
* @param {Object} [opt.srLog=null] 輸入事件紀錄物件,需提供函數info與error,各接收一紀錄物件,未提供時各自以空函數替代不進行紀錄,預設null
* @returns {Promise} 回傳Promise,resolve回傳列舉檔案陣列,各元素為檔案資訊物件,內含name、path、isFolder等鍵值,reject回傳錯誤訊息
* @example
*
* import downloadFiles from './src/downloadFiles.mjs'
*
* let st = {
* 'transportation': 'FTP',
* 'hostname': '{hostname}',
* 'port': 21,
* 'username': '{username}',
* 'password': '{password}',
* 'fdIni': './'
* }
*
* let opt = {
* srLog: {
* info: (msg) => {
* console.log('info', msg)
* },
* error: (msg) => {
* console.log('error', msg)
* },
* },
* }
*
* let vfps = await downloadFiles(st, `./_dwStorageTemp`, opt)
* .catch((err) => {
* console.log(err)
* })
* // console.log('vfps', vfps)
* // info { event: 'ftp.conn', msg: 'start...' }
* // info { event: 'ftp.conn', msg: 'done' }
* // info { event: 'ftp.ls', length: 2, msg: 'done' }
* // info { event: 'ftp.syncToLocal', msg: 'start...' }
* // ...
*
*/
let downloadFiles = async(st, fdDwStorageTemp, opt = {}) => {
let errTemp = null
//srLog
let srLog = get(opt, 'srLog', null)
//srLogInfo
let srLogInfo = get(srLog, 'info', null)
if (isfun(srLogInfo)) {
srLogInfo = (...args) => {
return srLog.info(...args)
}
}
else {
srLogInfo = () => {}
}
//srLogError
let srLogError = get(srLog, 'error', null)
if (isfun(srLogError)) {
srLogError = (...args) => {
return srLog.error(...args)
}
}
else {
srLogError = () => {}
}
//core
let core = async() => {
//fsCleanFolder
fsCleanFolder(fdDwStorageTemp)
//ftp
let ftp = WFtp({
...st,
timeLimit: 100 * 1000, //100s
})
// console.log('ftp', ftp)
//syncFiles
let syncFiles = async () => {
//ls
// let fps = await ftp.ls('.')
let fps = await ftp.ls(st.fdIni)
// console.log('ftp.ls', fps[0], fps.length)
srLogInfo({ event: 'ftp.ls', length: fps.length, msg: 'done' })
//syncToLocal
srLogInfo({ event: 'ftp.syncToLocal', msg: 'start...' })
let r = await ftp.syncToLocal(
st.fdIni,
fdDwStorageTemp,
(p) => {
// console.log('ftp.syncToLocal p', p.name, p.progress)
srLogInfo({ event: 'ftp.syncToLocal', name: p.name, progress: p.progress, msg: 'running...' })
},
{
levelLimit: 1, //僅下載第1層內檔案
forceOverwriteWhenSync: true, //強制全下載進行複寫本機數據
},
)
// console.log('ftp.syncToLocal r', r)
srLogInfo({ event: 'ftp.syncToLocal', result: r, msg: 'done' })
}
try {
//conn
srLogInfo({ event: 'ftp.conn', msg: 'start...' })
await ftp.conn()
srLogInfo({ event: 'ftp.conn', msg: 'done' })
//syncFiles
srLogInfo({ event: 'syncFiles', msg: 'start...' })
await syncFiles()
.then(() => {
srLogInfo({ event: 'syncFiles', msg: 'done' })
})
.catch((err) => { //須catch, 避免操作指令失敗造成程序中止
// console.log(err)
srLogError({ event: 'syncFiles', msg: getErrorMessage(err) })
})
}
finally {
//quit, 凡conn過即須執行至quit, w-ftp(1.0.17+)之quit會硬關閉socket, 否則FTP停滯時socket殘留致行程無法退出
//quit, 須catch, quit為收尾清理, 逾時路徑socket已被硬關閉, 失敗不應作廢已下載檔案
await ftp.quit()
.then((r) => {
// console.log('ftp.quit', r)
srLogInfo({ event: 'ftp.quit', result: r, msg: 'done' })
})
.catch((err) => {
// console.log(err)
srLogError({ event: 'ftp.quit', msg: getErrorMessage(err) })
})
}
}
//core
srLogInfo({ event: 'core', msg: 'start...' })
await core()
.then(() => {
srLogInfo({ event: 'core', msg: 'done' })
})
.catch((err) => { //須再catch, 避免無法連線SFTP時中止程序
// console.log(err)
srLogError({ event: 'core', msg: getErrorMessage(err) })
errTemp = err
})
//check
if (errTemp !== null) {
return Promise.reject(errTemp)
}
//vfps
let vfps = []
try {
srLogInfo({ event: 'getVfps', msg: 'start...' })
vfps = fsTreeFolder(fdDwStorageTemp, 1)
vfps = filter(vfps, (v) => {
return !v.isFolder //僅使用檔案
})
srLogInfo({ event: 'getVfps', length: vfps.length, msg: 'done' })
}
catch (err) {
// console.log(err)
srLogError({ event: 'getVfps', msg: getErrorMessage(err) })
errTemp = err
}
// console.log('vfps', vfps)
//check
if (errTemp !== null) {
return Promise.reject(errTemp)
}
return vfps
}
export default downloadFiles