import path from 'path'
import fs from 'fs'
import archiver from 'archiver'
import archiverEnc from 'archiver-zip-encrypted'
import unzipper from 'unzipper'
import get from 'lodash-es/get.js'
import genPm from 'wsemi/src/genPm.mjs'
import pmMap from 'wsemi/src/pmMap.mjs'
import getFileName from 'wsemi/src/getFileName.mjs'
import checkTarget from './checkTarget.mjs'
//registerFormat
archiver.registerFormat('zip-encrypted', archiverEnc)
/**
* Zip處理
*
* @class
* @returns {Object} 回傳壓縮物件,可使用函數zipFile、zipFolder、unzip
* @example
* import wz from 'w-zip'
*
* let fpUnzip = './testData/outputZip'
* let fpUnzipExtract = fpUnzip + '/extract'
*
* let fpSrc1 = './testData/input/file1(中文).txt'
* let fpZip1 = fpUnzip + '/test1.zip'
*
* let fpSrc2 = './testData/input/folder1'
* let fpZip2 = fpUnzip + '/test2.zip'
* let fpZip2PW = fpUnzip + '/test2PW.zip'
* let pw = 'abc'
*
* async function test() {
*
* //zipFile
* console.log('zipFile before')
* console.log('zipFile', await wz.mZip.zipFile(fpSrc1, fpZip1))
* console.log('zipFile after')
*
* //zipFolder
* console.log('zipFolder before')
* console.log('zipFolder', await wz.mZip.zipFolder(fpSrc2, fpZip2))
* console.log('zipFolder after')
*
* //zipFolder with password
* console.log('zipFolder with password before')
* console.log('zipFolder with password', await wz.mZip.zipFolder(fpSrc2, fpZip2PW, { pw }))
* console.log('zipFolder with password after')
*
* //unzip
* console.log('unzip1 before')
* console.log('unzip1', await wz.mZip.unzip(fpZip1, fpUnzipExtract + '/test1'))
* console.log('unzip1 after')
*
* //unzip
* console.log('unzip2 before')
* console.log('unzip2', await wz.mZip.unzip(fpZip2, fpUnzipExtract + '/test2'))
* console.log('unzip2 after')
*
* //unzip with password
* console.log('unzip2 with password before')
* console.log('unzip2 with password', await wz.mZip.unzip(fpZip2PW, fpUnzipExtract + '/test2PW', { pw }))
* console.log('unzip2 with password after')
*
* }
* test()
* .catch((err) => {
* console.log(err)
* })
*
* // zipFile before
* // zipFile done: test1.zip
* // zipFile after
* // zipFolder before
* // zipFolder done: test2.zip
* // zipFolder after
* // zipFolder with password before
* // zipFolder with password done: test2PW.zip
* // zipFolder with password after
* // unzip1 before
* // unzip1 done: test1
* // unzip1 after
* // unzip2 before
* // unzip2 done: test2
* // unzip2 after
* // unzip2 with password before
* // unzip2 with password done: test2PW
* // unzip2 with password after
*/
function mZip() {
/**
* 壓縮檔案
*
* @memberof mZip
* @param {String} fpSrc 輸入壓縮來源檔案位置字串
* @param {String} fpTar 輸入壓縮目標檔案位置字串
* @param {Object} [opt={}] 輸入設定物件,預設{}
* @param {Integer} [opt.level] 輸入壓縮程度整數,範圍為0至9,0為不壓縮而9為最高壓縮,預設9
* @param {String} [opt.pw] 輸入壓縮密碼字串,預設''
* @returns {Promise} 回傳Promise,resolve為成功資訊,reject為失敗資訊
*/
async function zipFile(fpSrc, fpTar, opt = {}) {
//check
if (!fs.existsSync(fpSrc)) {
return Promise.reject('invalid path of source file')
}
if (!fs.lstatSync(fpSrc).isFile()) {
return Promise.reject('path of source is not file')
}
//check fpTar
if (!checkTarget(fpTar)) {
return Promise.reject('invalid fpSrc')
}
//default
let level = get(opt, 'level', 9)
let pw = get(opt, 'pw', '')
try {
//pm
let pm = genPm()
//check
if (!fs.existsSync(path.dirname(fpTar))) {
fs.mkdirSync(path.dirname(fpTar), { recursive: true })
}
//archiver
let output = fs.createWriteStream(fpTar)
let archive
if (pw === '') {
archive = archiver('zip', {
zlib: { level },
})
}
else {
archive = archiver('zip-encrypted', {
zlib: { level },
encryptionMethod: 'zip20', //只能用zip20, 因unzipper目前不支援aes256
password: pw,
})
}
output.on('close', function() {
//console.log(archive.pointer() + ' total bytes')
//resolve
pm.resolve('done: ' + fpTar)
})
archive.on('warning', function(warn) {
//console.log('warning', warn)
})
archive.on('error', function(err) {
Promise.reject(err)
})
//pipe
archive.pipe(output)
//file
archive.file(fpSrc, { name: path.basename(fpSrc) })
//finalize
archive.finalize()
return pm
}
catch (err) {
return Promise.reject(err)
}
}
/**
* 壓縮資料夾
*
* @memberof mZip
* @param {String} fpSrc 輸入壓縮來源資料夾位置字串
* @param {String} fpTar 輸入壓縮目標資料夾位置字串
* @param {Integer} level 輸入壓縮程度整數,範圍為0至9,0為不壓縮而9為最高壓縮,預設9
* @returns {Promise} 回傳Promise,resolve為成功資訊,reject為失敗資訊
*/
async function zipFolder(fpSrc, fpTar, opt = {}) {
//check
if (!fs.existsSync(fpSrc)) {
return Promise.reject('invalid path of source file')
}
if (!fs.lstatSync(fpSrc).isDirectory()) {
return Promise.reject('path of source is not folder')
}
//check fpTar
if (!checkTarget(fpTar)) {
return Promise.reject('invalid fpSrc')
}
//default
let level = get(opt, 'level', 9)
let pw = get(opt, 'pw', '')
try {
//pm
let pm = genPm()
//check
if (!fs.existsSync(path.dirname(fpTar))) {
fs.mkdirSync(path.dirname(fpTar), { recursive: true })
}
//archiver
let output = fs.createWriteStream(fpTar)
let archive
if (pw === '') {
archive = archiver('zip', {
zlib: { level },
})
}
else {
archive = archiver('zip-encrypted', {
zlib: { level },
encryptionMethod: 'zip20', //只能用zip20, 因unzipper目前不支援aes256
password: pw,
})
}
output.on('close', function() {
//console.log(archive.pointer() + ' total bytes')
//resolve
pm.resolve('done: ' + fpTar)
})
archive.on('warning', function(warn) {
//console.log('warning', warn)
})
archive.on('error', function(err) {
Promise.reject(err)
})
//pipe
archive.pipe(output)
//directory
archive.directory(fpSrc, path.basename(fpSrc))
//finalize
archive.finalize()
return pm
}
catch (err) {
return Promise.reject(err)
}
}
/**
* 解壓縮檔案至資料夾
*
* @memberof mZip
* @param {String} fpSrc 輸入解壓縮來源檔案位置字串
* @param {String} fpTar 輸入解壓縮目標資料夾位置字串
* @param {Object} [opt={}] 輸入設定物件,預設{}
* @param {String} [opt.pw=''] 輸入解壓縮密碼字串,預設''
* @returns {Promise} 回傳Promise,resolve為成功資訊,reject為失敗資訊
*/
async function unzip(fpSrc, fpTar, opt = {}) {
//check
if (!fs.existsSync(fpSrc)) {
return Promise.reject('invalid path of source file')
}
if (!fs.lstatSync(fpSrc).isFile()) {
return Promise.reject('path of source is not file')
}
//check fpTar
if (!checkTarget(fpTar)) {
return Promise.reject('invalid fpSrc')
}
//default
let pw = get(opt, 'pw', '')
//extract
async function extract(fpSrc, fpTar, password) {
//mkdir
async function mkdir(fd) {
try {
//check
if (!fs.existsSync(fd)) {
fs.mkdirSync(fd, { recursive: true })
}
}
catch (err) {
return Promise.reject(err)
}
return 'done'
}
//extractFile
async function extractFile(file, fn, password = '') {
try {
//check
if (!fs.existsSync(path.dirname(fn))) {
fs.mkdirSync(path.dirname(fn), { recursive: true })
}
//buffer
let b = await file.buffer(password)
//writeFileSync
fs.writeFileSync(fn, b)
}
catch (err) {
return Promise.reject(err)
}
return 'done'
}
//directory
let directory = await unzipper.Open.file(fpSrc)
//pmMap
return pmMap(directory.files, async (file) => {
let p = path.join(fpTar, file.path)
if (file.type === 'File') {
return extractFile(file, p, password)
}
else {
return mkdir(p)
}
})
}
try {
//extract
await extract(fpSrc, fpTar, pw)
return Promise.resolve('done: ' + getFileName(fpTar))
}
catch (err) {
return Promise.reject(err)
}
}
return {
zipFile,
zipFolder,
unzip,
}
}
export default mZip()