1. 程式人生 > >node js批量匯出PDF 匯出壓縮為zip 拆分設計 檔案壓縮 批量匯出設計

node js批量匯出PDF 匯出壓縮為zip 拆分設計 檔案壓縮 批量匯出設計

node匯出pdf網上有不少介紹的文章,之前我也有過這方面的需求,總結一下遇到的問題,給大家點參考建議。

匯出PDF大概會遇到以下幾個階段:

1、匯出單個PDF檔案

2、匯出多個PDF檔案

下面著重說一下第二種情況,第一種情況比較簡單,直接用phantomjs匯出生成PDF即可。第二種情況,其實就是第一種情況的複雜化,我們先需要生成資料夾,在將生成的PDF放到資料夾中。這裡有個問題,就是如果大資料同時匯出生成會導致伺服器記憶體打滿,伺服器掛掉,這種情況下必須得重啟伺服器才行,連登入都沒法登入,宕機了。我自己親測的一個匯出3頁PDF的頁面大概會佔用20MB左右的記憶體,這個看你伺服器多大了,我設定的是同時匯出50個,50個成功後遞迴呼叫後續的,直到所有的都處理完。我把程式碼粘上面供大家參考一下,並且加上必要註釋,同時上傳專案到GIT上,有疑問的朋友可以留言一起學習探討一下。GIT地址:

https://github.com/seizeDev/node-pef

1、common.js

/**
 * Created by lizhen on 2017/11/13.
 */
var commonJs = {};
exports.commonJs = commonJs;
//匯出時生成的檔案戳
commonJs.reserverTime =  function () {
    var thisDate = new Date();
    var nameDate = thisDate.getFullYear().toString() + (thisDate.getMonth() + 1).toString() + thisDate.getDate().toString() + thisDate.getHours().toString() + thisDate.getMinutes().toString();
    return nameDate
};

// 匯出時生成的檔案大小
commonJs.byteConversion = function (byte) {
    try {
        if (typeof byte == 'number') {
            var calByte = byte;
            var returnType = 'b';
            if (calByte > 1024) {
                calByte = calByte / 1024;
                returnType = 'KB'
                if (calByte>1024){
                    calByte = calByte / 1024;
                    returnType = 'MB'
                    if(calByte>1024){
                        calByte = calByte / 1024;
                        returnType = 'GB'
                    }
                }
            }
            return calByte+returnType;
        }


    } catch (err) {
        return err
    }
};

// 解析傳入的請求引數
commonJs.parseResult = function(body) {
    var json = {};
    try {
        json = JSON.parse(body);
    } catch (e) {
        /*
         Error(e);
         console.log(Constant.ERROR, "解析返回資料出錯!返回資料為:", body);
         Res.end('{"code":500}');
         return;*/
    }
    var rs = json;
    return rs;
};

2、userMsg.js

/**
 * Created by lizhen on 2017/11/13.
 */
var userMsg = {};
//匯出檔案存放的地址
var dirPath = "/Users/lizhen/Desktop/resurce/export_policy";
userMsg.dirPath = dirPath;

//匯出檔案的名字
userMsg.htmlMaps = {
    '居間服務協議': 'intermediary_agreement',
}
//匯出伺服器資訊
const productionEnv = {
    hostname: '10.8.0.6',
    port: 8180,
    path: '/backstage/v1/sso/login'
}
userMsg.productionEnv = productionEnv;
//使用者資訊

//'lizhen_export', 'lizhenniubi', 'backstage_user'
userMsg.user = {
    username:'lizhen_export',
    pwd:'lizhenniubi',
    type:'backstage_user'
}
//當前第幾條
userMsg.total = 0;
// 匯出返回給前端的下載路徑
userMsg.exporttUrl = 'http://www.baidu.com';
userMsg.dataMsg = null;
userMsg.dataFile = null;

exports.userMsg = userMsg;

3、exportMethod.js

/**
 * Created by lizhen on 2017/11/13.
 */
var exportMethod = {};
exportMethod.exportPdf = function () {
    
}
//登入成功後的回撥生成PDF
var exec = require('child_process').exec;
var userMsg = require("../common/userMsg").userMsg;
var logger = require("../log4js/logHelper").helper;
var fs = require('fs');
exportMethod.loginCallback = function (data, order, pageHtml,yearRate , thisTime, cid ) {
    return new Promise(function (resolve, reject) {
        var productUrl = 'https://www.baidu.com';
        var url = null;
        if(!yearRate||yearRate === 'undefined'){
            url = `phantomjs savePdf.js "${productUrl}${pageHtml}.html?sid=${data.__sid}&timestamp=${data.timestamp}&signature=${data.signature}&orderNo=${order}&channel_id=${cid}" ${order} ${pageHtml} ${thisTime}`;
        }else{
            url = `phantomjs savePdf.js "${productUrl}${pageHtml}.html?sid=${data.__sid}&timestamp=${data.timestamp}&signature=${data.signature}&orderNo=${order}&yearRate=${yearRate}&channel_id=${cid}" ${order} ${pageHtml} ${thisTime}`;
        }
        logger.writeInfo(url);
        exec(url, {
                encoding: 'utf-8',
                timeout: 100000,
                maxBuffer: 200 * 1024,
                killSignal: 'SIGTERM',
                cwd: null,
                env: null
            },
            function (err, out) {
                userMsg.total += 1;
                var data = {
                    total:userMsg.total,
                    order
                }
                if (err) {
                    console.log(err);
                    data.success = false;
                    data.message = err;
                    resolve(data)
                } else {
                    data.success = true;
                    data.message = '請求成功'
                    resolve(data)
                }
            });
    })
}
/**
 * 全部都匯出成功後壓縮整個資料夾
 * @type {commonJs}
 */
var commonJs = require("../common/common").commonJs;
exportMethod.gzipPaf = function (dataMsg,thisName,thisTime) {
    return new Promise(function (resolve, reject) {
        if (dataMsg == null) {
            dataMsg = '請求成功'
        }
        var fileName = thisName + thisTime;
        var archiver = require('archiver');
        //noinspection JSUnresolvedFunction
        var output = fs.createWriteStream(userMsg.dirPath + '/' + fileName + '.zip');
        //noinspection SpellCheckingInspection
        var archive = archiver('zip', {
            zlib: {level: 9} // Sets the compression level.
        });
        output.on('close', function () {
            logger.writeInfo('匯出成功:總大小' + commonJs.byteConversion(archive.pointer()));
            //noinspection SpellCheckingInspection
            var rsolveData = {
                message: dataMsg,
                name: `${userMsg.exporttUrl}/${thisName}${thisTime}.zip`
            }
            resolve(rsolveData)
        });
        //noinspection JSUnresolvedFunction
        archive.on('warning', function (err) {
            //noinspection SpellCheckingInspection
            if (err.code === 'ENOENT') {
                // log warning
            } else {
                // throw error
                logger.writeErr('匯出錯誤:' + err);
            }
        });
        archive.on('error', function (err) {
            logger.writeErr('匯出失敗:' + err);
        });

        archive.pipe(output);
        var fsDir = userMsg.dirPath + '/' + fileName;
        fs.readdirSync(fsDir).forEach(function (file) {
            var pathname = userMsg.dirPath + '/' + fileName + '/' + file;
            //noinspection JSUnresolvedFunction
            archive.append(fs.createReadStream(pathname), {name: file});
        });
        //noinspection JSUnresolvedFunction
        archive.finalize();
    })
}


exportMethod.recursiveExport = function (orderList,config,thisName, rate, cid) {
    return new Promise(function (resolve, reject) {
        var thisTime = commonJs.reserverTime();
        logger.writeInfo(cid);
        /**
         * 如果沒有匯出的資料夾,則建立資料夾
         */
        if (!fs.existsSync(userMsg.dirPath + '/' + (thisName + thisTime))) {
            fs.mkdirSync(userMsg.dirPath + '/' + (thisName + thisTime));
        }
        orderList.forEach((order) => {
            exportMethod.loginCallback(config, order, thisName, rate, thisTime, cid).then(function (data) {
                logger.writeInfo(data);
                if (!data.success) {
                    userMsg.dataMsg += `${data.order}匯出失敗,請重新匯出|`;
                    userMsg.dataFile += `${thisName} + ${thisTime}|`;
                }
                //匯出成功!
                if (data.total == orderList.length) {
                    exportMethod.gzipPaf(userMsg.dataMsg,thisName,thisTime).then(exportMsg => {
                        userMsg.total = 0;
                        userMsg.dataMsg = null;
                        userMsg.dataFile = null;
                        resolve(exportMsg);
                    })
                }
            });
        })
    })
};



exports.exportMethod = exportMethod;

4、savePdf.js

var system = require('system');
var args = system.args;
var url = args['1'];
var name = args['2'];
var filename = args['3'];
var filetime = args['4'];
var dirPath = "/Users/lizhen/Desktop/resurce/export_policy";
openPage(url);

function openPage(url) {
    var page = require('webpage').create();
    page.open(url, function (status) {
        setTimeout(function () {
            console.log(status)
            if (status === "success") {
                page.paperSize = {
                    format: 'A4',
                    orientation: 'portrait',
                    border: '1cm'
                };
                page.render(dirPath+'/'+(filename+filetime)+'/'+name + ".pdf");
            } else {
                console.log("Page failed to load.");
            }
            phantom.exit(0);
        }, 3000);
    });
}
5、logHelper.js
/**
 * Created by lizhen on 2017/9/26.
 */
var helper = {};
exports.helper = helper;

var log4js = require('log4js');

// 目錄建立完畢,才載入配置,不然會出異常
log4js.configure({
    appenders: { cheese: { type: 'file', filename: 'cheese.log' } },
    categories: { default: { appenders: ['cheese'], level: 'ALL' } }
});

var logDebug = log4js.getLogger('logDebug');
var logInfo = log4js.getLogger('logInfo');
var logWarn = log4js.getLogger('logWarn');
var logErr = log4js.getLogger('logErr');

helper.writeDebug = function (msg) {
    if (msg == null)
        msg = "";
    logDebug.debug(msg);
};

helper.writeInfo = function (msg) {
    if (msg == null)
        msg = "";
    console.log(msg)
    logInfo.info(msg);
};

helper.writeWarn = function (msg) {
    if (msg == null)
        msg = "";
    console.log(msg)
    logWarn.warn(msg);
};

helper.writeErr = function (msg, exp) {
    if (msg == null)
        msg = "";
    if (exp != null)
        msg += "\r\n" + exp;
    console.log(msg)
    logErr.error(msg);
};

// 配合express用的方法
exports.use = function (app) {
    //頁面請求日誌, level用auto時,預設級別是WARN
    app.use(log4js.connectLogger(logInfo, {level: 'debug', format: ':method :url'}));
}
6、finlinkAgreement.js
// var http = require("http");
var https = require("https");
var fs = require("fs");
var md5 = require("md5");
var express = require('express');
var logger = require("./log4js/logHelper").helper;
var userMsg = require("./common/userMsg").userMsg;
var commonJs = require("./common/common").commonJs;
var exportMethod = require("./export/exportMethod").exportMethod;
var log = require('./log4js/logHelper');
var app = express();
log.use(app);
var exportHtml = null;
var yearRate = null;
var channelId = null;
var exportList = [];
var allList = [];
app.get('/getPdf', function (req, res) {
    logger.writeInfo("開始記錄日誌");
    userMsg.total = 0;
    logger.writeInfo('/getPdf');
    try {
        allList = req.query.order_nos.split(',');
        logger.writeInfo(allList);
    } catch(error) {
        res.send({
            'code':100,
            'message':'輸入的列表分隔符錯誤!'
        });
        logger.writeErr(req.query.order_nos);
        return;
    }
    exportHtml = req.query.html_name;
    yearRate = req.query.year_rate;
    channelId = req.query.channelId;
    logger.writeInfo(exportHtml);
    post(userMsg.user.username,userMsg.user.pwd,userMsg.user.type).then(function (result) {
        logger.writeInfo(result);
        res.send(result);
    })
});
var globalMsg = [];
function post(username, pwd, type) {
    return new Promise(function (resolve, reject) {
        var params = {
            username: username,
            password: pwd,
            type: type
        };
        var options = {
            hostname: userMsg.productionEnv.hostname,
            port: userMsg.productionEnv.port,
            path: userMsg.productionEnv.path,
            method: 'POST',
            headers: {
                "Content-Type": 'application/json;charset=utf-8'
            }
        };
        //登入
        var req = https.request(options, function (res) {
            var body = "";
            res.setEncoding('utf-8');
            res.on('data', function (chunk) {
                body += chunk;
            }).on("end", function () {
                var data = commonJs.parseResult(body);
                var config = {};
                var timestamp = (new Date()).valueOf();
                config.__sid = data.data.__sid;
                config.timestamp = timestamp;
                config.signature = md5(data.data.key + timestamp.toString());
                /**
                 * 根據使用者傳入的HTML名字來判斷匯出哪個檔案,如果沒有的話,則預設為intermediary_agreement
                 * @type {string}
                 */
                var thisName = exportHtml ? userMsg.htmlMaps[exportHtml] : 'intermediary_agreement';
                function listTraverse(all,data,name,rate,cid) {
                    var angentList = all.splice(0, 50);
                    exportMethod.recursiveExport(angentList, data, name, rate, cid).then((exportMsg) => {
                        globalMsg.push(exportMsg);
                        logger.writeInfo(exportMsg);
                        if (all.length > 50) {
                            listTraverse(all, data, name)
                        } else if (all.length > 0 && all.length <= 50) {
                            listTraverse(all, data, name);
                            all = [];
                        } else {
                            resolve(globalMsg)
                            globalMsg = [];
                        }
                    })
                }
                listTraverse(allList,config,thisName, yearRate, channelId);
            });
        });
        req.write(JSON.stringify(params));
        req.end();
    })
}


app.listen(8089, function () {
    console.log('監聽8089')
});

7、fileCopy.js

var fs=require('fs');
var exec = require('child_process').exec;
var args = process.argv;
var filePath, dirPath, outputdir;
while(args.length){
   switch(args.shift()){
        case "-f":
      case "--file":
         filePath = args.shift();
      break;
      case "-d":
      case "--dir":
         dirPath = args.shift();
      break;
      case "-od":
      case "--outputdir":
         outputdir = args.shift();
      break;
   }
}

var dirs = fs.readdirSync(dirPath);
var text = fs.readFileSync(filePath, 'UTF-8').match(/\S+/g);

dirs.forEach(function(dir){
   for(var i=0, len=text.length;i<len;i++){
      if(new RegExp(text[i]).test(dir)){
         console.log(["xcopy", '"'+dirPath+"/"+dir+'"', '"'+outputdir+"/"+dir+'"', "/e /Y /I"].join(' '));
         exec(["xcopy", '"'+dirPath+"/"+dir+'"', '"'+outputdir+"/"+dir+'"', "/e /Y /I"].join(' '), function(dir, err){
            if(err){
               console.log("程式出錯!");
               throw err;
            }
            console.log(dir, "拷貝完成");
         }.bind(null, dirPath+"/"+dir));
      }
   }
});