Restify:复制文件并使用Promise链查询数据库时出现套接字挂断错误

问题描述 投票:1回答:2

我正在使用restify框架来构建一个小型应用,该应用将上传的文件从其临时位置复制到永久位置,然后将该新位置插入MySQL数据库。但是,当尝试复制文件然后运行承诺的查询时,系统会抛出一个未被诺言链捕获的静默错误,从而在Web服务器端造成502错误。下面是一个最小的工作示例。这个例子已经过测试,但确实失败了。

如果删除了过程中的步骤之一(复制文件或将字符串存储在数据库中,则静默错误消失,并发送API响应。但是,这两个步骤对于以后的文件检索都是必需的。

主要重新整理文件

const restify = require('restify');
const corsMiddleware = require('restify-cors-middleware');
const cookieParser = require('restify-cookies');

const DataBugsDbCredentials = require('./config/config').appdb;
const fs = require('fs');
const { host, port, name, user, pass } = DataBugsDbCredentials;
const database = new (require('./lib/database'))(host, port, name, user, pass);

const server = restify.createServer({
    name: 'insect app'
});

// enable options response in restify (anger) -- this is so stupid!! (anger)
const cors = corsMiddleware({});
server.pre(cors.preflight);
server.use(cors.actual);

// set query and body parsing for access to this information on requests
server.use(restify.plugins.acceptParser(server.acceptable));
server.use(restify.plugins.queryParser({ mapParams: true }));
server.use(restify.plugins.bodyParser({ mapParams: true }));
server.use(cookieParser.parse);


server.post('/test', (req, res, next) => {
    const { files } = req;

    let temporaryFile = files['file'].path;
    let permanentLocation = '/srv/www/domain.com/permanent_location';

    // copy file 
    return fs.promises.copyFile(temporaryFile, permanentLocation)

        // insert into database
        .then(() => database.query(
            `insert into Specimen (
                CollectorId,
                HumanReadableId,
                FileLocation
            ) values (
                1,
                'AAA004',
                ${permanentLocation}
            )`
        ))
        .then(() => {
            console.log('success!!!')
            return res.send('success!')
        })
        .catch(error => {
            console.error(error)
            return res.send(error);
        });
});

./ lib / database.js

'use strict';

const mysql = require('mysql2');

class Database {
    constructor(host, port, name, user, pass) {
        this.connection = this.connect(host, port, name, user, pass);
        this.query = this.query.bind(this);
    }

    /**
     * Connects to a MySQL-compatible database, returning the connection object for later use
     * @param {String} host The host of the database connection
     * @param {Number} port The port for connecting to the database
     * @param {String} name The name of the database to connect to
     * @param {String} user The user name for the database
     * @param {String} pass The password for the database user
     * @return {Object} The database connection object
     */
    connect(host, port, name, user, pass) {
        let connection = mysql.createPool({
            connectionLimit : 20,
            host            : host,
            port            : port,
            user            : user,
            password        : pass,
            database        : name,
            // debug           : true
        });

        connection.on('error', err => console.error(err));
        return connection;
    }

    /**
     * Promisifies database queries for easier handling
     * @param {String} queryString String representing a database query
     * @return {Promise} The results of the query
     */
    query(queryString) {
        // console.log('querying database');
        return new Promise((resolve, reject) => {
            // console.log('query promise before query, resolve', resolve);
            // console.log('query promise before query, reject', reject);
            // console.log('query string:', queryString)
            this.connection.query(queryString, (error, results, fields) => {
                console.log('query callback', queryString);
                console.error('query error', error, queryString);
                if (error) {
                    // console.error('query error', error);
                    reject(error);
                } else {
                    // console.log('query results', results);
                    resolve(results);
                }
            });
        });
    }
}

module.exports = Database;

./ testfile.js(用于快速查询restify API)

'use strict';

const fs = require('fs');
const request = require('request');

let req = request.post({
    url: 'https://api.databugs.net/test',
}, (error, res, addInsectBody) => {
    if (error) {
        console.error(error);
    } else {
        console.log('addInsectBody:', addInsectBody);
    }
});
let form = req.form();
form.append('file', fs.createReadStream('butterfly.jpg'), {
    filename: 'butterfly.jpg',
    contentType: 'multipart/form-data'
});

如果向本地主机发出请求,则抛出“ ECONNRESET”错误,如下所示:

Error: socket hang up
    at connResetException (internal/errors.js:570:14)
    at Socket.socketOnEnd (_http_client.js:440:23)
    at Socket.emit (events.js:215:7)
    at endReadableNT (_stream_readable.js:1183:12)
    at processTicksAndRejections (internal/process/task_queues.js:80:21) {
  code: 'ECONNRESET'
}

仅当promise链中同时存在数据库和文件I / O时,才会引发此错误。此外,如果首先发出数据库请求而第二次发生文件I / O,则不会发生该错误;但是,对服务器的另一个快速请求将立即导致“ ECONNRESET”错误。

mysql node.js promise fs restify
2个回答
0
投票

到目前为止,我发现的唯一解决方案是切换文件I / O和数据库查询,以便最后执行文件I / O操作。此外,将文件I / O操作更改为重命名而不是复制文件可防止连续的API查询快速抛出相同的错误(在进行任何非重命名的文件I / O操作之后迅速进行数据库查询) )。不幸的是,我对OP中的套接字挂起没有合理的解释,但是下面是OP修改后的代码以使其正常运行。

const restify = require('restify');
const corsMiddleware = require('restify-cors-middleware');
const cookieParser = require('restify-cookies');

const DataBugsDbCredentials = require('./config/config').appdb;
const fs = require('fs');
const { host, port, name, user, pass } = DataBugsDbCredentials;
const database = new (require('./lib/database'))(host, port, name, user, pass);

const server = restify.createServer({
    name: 'insect app'
});

// enable options response in restify (anger) -- this is so stupid!! (anger)
const cors = corsMiddleware({});
server.pre(cors.preflight);
server.use(cors.actual);

// set query and body parsing for access to this information on requests
server.use(restify.plugins.acceptParser(server.acceptable));
server.use(restify.plugins.queryParser({ mapParams: true }));
server.use(restify.plugins.bodyParser({ mapParams: true }));
server.use(cookieParser.parse);


server.post('/test', (req, res, next) => {
    const { files } = req;

    let temporaryFile = files['file'].path;
    let permanentLocation = '/srv/www/domain.com/permanent_location';

    // copy file 
    // insert into database
    return database.query(
            `insert into Specimen (
                CollectorId,
                HumanReadableId,
                FileLocation
            ) values (
                1,
                'AAA004',
                ${permanentLocation}
            )`
        )
        .then(() => fs.promises.rename(temporaryFile, permanentLocation))
        .then(() => {
            console.log('success!!!')
            return res.send('success!')
        })
        .catch(error => {
            console.error(error)
            return res.send(error);
        });
});

-1
投票

您当时没有处理数据库承诺并捕获-

主要重新整理文件


const restify = require('restify');
const corsMiddleware = require('restify-cors-middleware');
const cookieParser = require('restify-cookies');

const DataBugsDbCredentials = require('./config/config').appdb;
const fs = require('fs');
const { host, port, name, user, pass } = DataBugsDbCredentials;
const database = new (require('./lib/database'))(host, port, name, user, pass);

const server = restify.createServer({
    name: 'insect app'
});

// enable options response in restify (anger) -- this is so stupid!! (anger)
const cors = corsMiddleware({});
server.pre(cors.preflight);
server.use(cors.actual);

// set query and body parsing for access to this information on requests
server.use(restify.plugins.acceptParser(server.acceptable));
server.use(restify.plugins.queryParser({ mapParams: true }));
server.use(restify.plugins.bodyParser({ mapParams: true }));
server.use(cookieParser.parse);


server.post('/test', (req, res, next) => {
    const { files } = req;

    let temporaryFile = files['file'].path;
    let permanentLocation = '/srv/www/domain.com/permanent_location';

    // copy file 
    return fs.promises.copyFile(temporaryFile, permanentLocation)

        // insert into database
        .then(() =>{ 
                // Your database class instance query method returns promise 
                database.query(
                `insert into Specimen (
                    CollectorId,
                    HumanReadableId,
                    FileLocation
                ) values (
                    1,
                    'AAA004',
                    ${permanentLocation}
                )`
                ).then(() => {
                    console.log('success!!!')
                    return res.send('success!')
                })
                .catch(error => {
                    console.error('Inner database promise error', error)
                    return res.send(error);
                });
            }).catch(error => {
                console.error('Outer fs.copyfile promise error', error)
                return res.send(error);
            })

});

© www.soinside.com 2019 - 2024. All rights reserved.