web開發-前端(angular)download, server(nodejs)端用python和相關插件生成report,

請求流程: web --> nodejs --> python(generate excel) --> Mongodb

Python 2.7.5
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]

// install below 5 plugin
sudo python setup.py install
mongo-python-driver-master/
functools32-3.2.3-2
vcversioner-2.16.0.0
jsonschema-2.6.0
XlsxWriter-0.8.5

 

python -c "import pymongo; print(pymongo.version); print(pymongo.has_c())"
3.6.0.dev0
False

which python
python --version
// scl can switch the python version
source scl_source enable rh-python36

 

前端:Angular

後端:nodejs(v8.11),python(Python 2.7.5)

 

----------------------------------

具體示例如下:

前端:

1. angular發起異步請求到nodejs,在http請求中提供相關參數,比如

// page Component
    public downloadExcelFile() {
		let parameter = {
			pyName: 'pyName',
			parameterForPy: {
						date: '',
						Name: '',
						outPutPath: ''
					}
		}

        this.downloadServ.downloadExcelByPy(parameter).subscribe(data => {
            
            if (window.navigator.msSaveOrOpenBlob) {
                navigator.msSaveBlob(data, fileName);
            } else {
                var link = document.createElement('a');
                link.href = window.URL.createObjectURL(data);
                link.download = fileName;
                link.click();
                window.URL.revokeObjectURL(link.href);
            }
        });
    }



// downloadServ Service
public downloadExcelByPy(parameter) {
    let body = {
        pyName: parameter.pyName,
        parameterForPy: parameter.parameterForPy
    }

	let headers = new HttpHeaders();

	return this._http.post('/test/downloadExcelByPy', body, { headers: headers, observe: 'response', responseType: "blob" }).map(response => {
				if (response["status"] == 400) {
					console.error("400 error");
				} else if (response["status"] == 200) {
					let contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
					let blob = new Blob([(<any>response).body], { type: contentType });
					return blob;
				}
			});
}

 

後端:

在nodejs端,調用python相關程序,生成report,並放置到指定的目錄下,並用stream流讀取返回到resp裏

此處,在nodejs端用spawn方式調用python的程序,並且nodejs和python程序約定好輸入和輸出參數即可

比如,retData[0] === 91 && retData[1] === 48即爲約定參數,代表是否成功生成report

const fs = require('fs');
const path = require('path');
var spawn = require('child_process').spawn;

module.exports.downloadExcelByPy = function(req, res) {

    const outPutPath = '.../reportGeneratorPath/';
    let py = spawn('python', ['pyfile_root_path/pyreportGenerator.py']);
    let parameterForPy = req.body.parameterForPy;
    parameterForPy.outPutPath = outPutPath;

    let pythonReturn;
    let pythonErr;
    let isSuccess = false;

    logger.info('userName', 'downloadExcelByPy', 'Start...');

    py.stdin.write(JSON.stringify(parameterForPy));

    py.stdin.end();

    py.stdout.on('data', function(retData) {

        logger.info('userName', 'downloadExcelByPy > stdout data', 'retData: ' + retData.toString());

        if (retData[0] === 91 && retData[1] === 48) {
            pythonReturn = JSON.parse("{\"ret\":" + retData.toString().replace(/\'/g, "\"") + "}").ret;
            isSuccess = true;
        }
    });

    py.stdout.on('end', function() {

        if (isSuccess) {

            logger.info('userName', 'downloadExcelByPy > stdout end', 'isSuccess:' + isSuccess + '; pythonReturn[1]: ' + pythonReturn[1]);
            let fileName = pythonReturn[1];
            let filePath = path.join(outPutPath, fileName);
            let stats = fs.statSync(filePath);
        
            logger.info('userName', 'getFile', 'Start...');
        
            if (stats.isFile()) {
                res.set({
                    'Content-Type': 'application/octet-stream',
                    'Content-Disposition': 'attachment; filename=' + fileName,
                    'Content-Length': stats.size
                });
        
                logger.info('userName', 'getFile > isFile', 'End, Ready to response...');

                const srcReadStream = fs.createReadStream(filePath);

                res.status(200);
                srcReadStream.pipe(res);

                srcReadStream.on('error', function (err) {
                    logger.error(...);
                    srcReadStream.destroy();
                });
                srcReadStream.on('close', function () {
                    logger.info('userName', 'downloadExcelByPy', 'End.');
                    srcReadStream.destroy();
                });

                // fs.createReadStream(filePath).pipe(res);
            } else {
        
                logger.info('userName', 'getFile > is NOT File', 'End, 404 to response...');
                res.end(404);
            }
        } else {
            let err = {};
            err.messageType = _RMSCONSTS.TYPE_ERROR_PY_GEN_FAILED;
            err.language = _RMSCONSTS.MULTI_LANGUAGE_JP;
            logger.info('userName', 'downloadExcelByPy', 'End, 404 to response...' + err);
            res.end(404);
        }
    });

    py.stderr.on('data', function(errData) {

        logger.info('userName', 'downloadExcelByPy > stderr data', 'errData: ' + errData.toString());
        pythonErr = errData;
    });
};

----------------------------------

上面的示例,類似於這篇文章的download,是一個道理

https://blog.jayway.com/2017/07/13/open-pdf-downloaded-api-javascript/

How to open a pdf downloaded from an API with JavaScript

July 13, 2017 by Anders Poulsen in JavaScriptWeb | 38 Comments

Anders Poulsen

On my most recent assignment I was faced with the challenge of downloading a pdf file from an API and displaying it in the browser.

Normally, the API would provide a json-object with a link to the pdf, which can then be used to open a new browser tab/window and the browser or platform takes care of the rest.

In this case however the API was designed for native mobile apps and required the presence of a custom ‘api-key’ http-header, which made it impossible to just provide the url to the browser.

To solve this it was necessary to download the file to memory with an XHR request and then get the browser to open or download it with whatever plugin/UI it normally uses for pdf file.

For the XHR request we use the Fetch API with the whatwg-fetch polyfill. In essence the Fetch API fetch() method returns a response, from which a blob can be created. This blob object can be use to create an objectURL, which can then be used as href in a link.

showFile(blob){
  // It is necessary to create a new blob object with mime-type explicitly set
  // otherwise only Chrome works like it should
  var newBlob = new Blob([blob], {type: "application/pdf"})

  // IE doesn't allow using a blob object directly as link href
  // instead it is necessary to use msSaveOrOpenBlob
  if (window.navigator && window.navigator.msSaveOrOpenBlob) {
    window.navigator.msSaveOrOpenBlob(newBlob);
    return;
  } 

  // For other browsers: 
  // Create a link pointing to the ObjectURL containing the blob.
  const data = window.URL.createObjectURL(newBlob);
  var link = document.createElement('a');
  link.href = data;
  link.download="file.pdf";
  link.click();
  setTimeout(function(){
    // For Firefox it is necessary to delay revoking the ObjectURL
    window.URL.revokeObjectURL(data);
  , 100}
}

fetch([url to fetch], {[options setting custom http-headers]})
.then(r => r.blob())
.then(showFile)

This solution works on Chrome, Safari, Firefox, Opera, IE11 and Edge.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章