fsMergeFilesCore.mjs

// import fs from 'fs'
import get from 'lodash-es/get.js'
import genPm from './genPm.mjs'
import isearr from './isearr.mjs'
import getPathParent from './getPathParent.mjs'
import fsIsFolderCore from './fsIsFolderCore.mjs'
import fsIsFileCore from './fsIsFileCore.mjs'
import fsCreateFolderCore from './fsCreateFolderCore.mjs'
import fsDeleteFileCore from './fsDeleteFileCore.mjs'


/**
 * 後端nodejs合併多檔案
 *
 * Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/fsMergeFilesCore.test.mjs Github}
 * @memberOf wsemi
 * @param {String} fn 輸入實際檔名字串
 * @param {Array} fpsIn 輸入合併前各切片檔案路徑陣列
 * @param {String} fpOut 輸入合併後檔案路徑字串
 * @returns {Promise} 回傳Promise,resolve回傳合併後物件,reject回傳錯誤訊息
 * @example
 * //need test in nodejs
 *
 * //see fsMergeFiles
 *
 */
async function fsMergeFilesCore(fn, fpsIn, fpOut, opt = {}) {
    let errTemp = ''

    //fs
    let fs = get(opt, 'fs')

    //check fpsIn
    if (!isearr(fpsIn)) {
        throw new Error(`fpsIn in not an effective array`)
    }

    //getPathParent
    let fd = getPathParent(fpOut)

    //check
    if (!fsIsFolderCore(fd, { fs })) {
        fsCreateFolderCore(fd, { fs })
    }

    //pm
    let pm = genPm()

    //攔截錯誤, 注意stream是非同步故try catch是無法攔截的, 須各自監聽read與write串流的error事件處理, 此處是攔截串流以外的錯誤
    try {

        //streamWrite
        let streamWrite = fs.createWriteStream(fpOut)

        // pathUploadTemp, packageId, chunkTotal, filename,

        for (let i = 0; i < fpsIn.length; i++) {

            //fpIn
            let fpIn = fpsIn[i]
            // console.log('fpIn', fpIn)

            //check
            if (!fsIsFileCore(fpIn, { fs })) {
                throw new Error(`fpIn[${fpIn}] is not a file`)
            }

            //使用readFileSync會忽略背壓, 若寫入相對慢就會儲存至記憶體, 導致記憶體超量使用, 得要偵測與控制背壓
            // //chunkData
            // let chunkData = fs.readFileSync(fpIn)
            // //write
            // streamWrite.write(chunkData)
            // //fsDeleteFileCore
            // fsDeleteFileCore(fpIn)

            //transfer
            let transfer = () => {
                let pmc = genPm()

                //streamRead
                let streamRead = fs.createReadStream(fpIn)
                // console.log('fpIn',fpIn)

                //監測錯誤
                streamRead.on('error', (err) => {
                    errTemp = err.message
                    pmc.reject(err)
                })

                //監測串流結束
                streamRead.on('end', () => {
                    fsDeleteFileCore(fpIn, { fs })
                    pmc.resolve()
                })

                //pipe, 將讀取流接入寫入流, 會自動調節讀寫速率處理背壓
                //注意pipe不會自動處理錯誤, 若read出錯也不會因pipe轉移至write, 故read的錯誤得要獨立監聽處理
                streamRead.pipe(streamWrite, { end: false })

                return pmc
            }
            await transfer()

        }

        //end
        streamWrite.end()

        //error, 若有error則不會觸發finish
        streamWrite.on('error', (err) => {
            // console.log(`merge filename[${filename}] err`, err)
            errTemp = err.message
            pm.reject(errTemp)
        })

        //finish, end之後檔案未必完成寫入會有時間差, 得要監聽finish才能確定寫入檔案完成
        streamWrite.on('finish', () => {
            // console.log(`merge filename[${filename}] end`)

            //r
            let r = {
                filename: fn,
                path: fpOut,
            }
            // let s = fs.statSync(fpOut)
            // console.log('s.size', s.size)

            //resolve
            pm.resolve(r)

        })

    }
    catch (err) {
        errTemp = err.message
        pm.reject(errTemp)
    }

    return pm
}


export default fsMergeFilesCore