Node.js用ssh2-sftp-client定时上传报错:已有SFTP连接存在
问题:ssh2-sftp-client重复上传报错“An existing SFTP connection is already defined”
在Node.js应用中使用ssh2-sftp-client工具,每5秒执行一次图片上传操作。首次上传成功,但重复执行时触发错误:Error: connect: An existing SFTP connection is already defined。
原代码如下:
let Client = require('ssh2-sftp-client'); let sftp = new Client(); function uploadScreenshot() { randomNumber = Buffer.from(Math.random().toString()).toString("base64").substring(10,15); filename = "test_screenshot_upload"+randomNumber+".png"; sftp.connect({ host: 'ssh.strato.de', port: '22', username: 'user', password: 'pass' }).then(() => { sftp.put('screenshot.png', '/path/folder/folder/'+filename); }).then(data => { console.log("uploaded screenshot to server as " + filename); }).catch(err => { console.log(err, 'catch error'); }); } function foo() { uploadScreenshot(); setInterval(foo, 5000); } foo();
尝试在sftp.put后添加sftp.end()但未解决问题,需要排查原因及修复方案。
问题原因与修复方案
核心原因
- 重复调用connect():仅创建了一个全局
sftp实例,第一次调用uploadScreenshot时已通过connect()建立连接,后续重复调用connect()会触发“已有连接存在”的报错。 - Promise链式调用错误:
sftp.put()是异步Promise操作,原代码未返回该Promise,导致后续then在上传未完成时就执行;即便添加sftp.end(),也会因提前关闭连接导致上传失败或连接状态异常。 - 定时器逻辑错误:
foo()内部调用setInterval(foo, 5000)会不断创建新定时器,最终上传频率会越来越高,引发更多连接问题。
修复方案
方案1:每次上传创建新实例(简单直观)
每次上传时创建新的SFTP客户端实例,上传完成(或出错)后关闭连接,避免重复使用同一实例:
let Client = require('ssh2-sftp-client'); function uploadScreenshot() { // 每次上传创建新的客户端实例 const sftp = new Client(); const randomNumber = Buffer.from(Math.random().toString()).toString("base64").substring(10,15); const filename = `test_screenshot_upload${randomNumber}.png`; sftp.connect({ host: 'ssh.strato.de', port: 22, username: 'user', password: 'pass' }) // 必须return put的Promise,保证链式调用顺序 .then(() => sftp.put('screenshot.png', `/path/folder/folder/${filename}`)) .then(() => { console.log(`uploaded screenshot to server as ${filename}`); // 上传完成后关闭连接 return sftp.end(); }) .catch(err => { console.log(err, 'catch error'); // 出错时也要关闭连接,避免资源泄漏 sftp.end(); }); } // 直接设置定时器调用上传函数,避免嵌套创建多个定时器 setInterval(uploadScreenshot, 5000); // 首次立即执行上传 uploadScreenshot();
方案2:复用长连接(更高效)
只在首次上传时建立连接,后续复用该连接;若连接断开,自动尝试重连:
let Client = require('ssh2-sftp-client'); const sftp = new Client(); let isConnected = false; // 封装连接逻辑,确保仅在未连接时建立连接 function connectSFTP() { if (isConnected) return Promise.resolve(); return sftp.connect({ host: 'ssh.strato.de', port: 22, username: 'user', password: 'pass' }) .then(() => { isConnected = true; console.log('SFTP连接已建立'); }) .catch(err => { console.log('SFTP连接失败', err); isConnected = false; throw err; }); } function uploadScreenshot() { const randomNumber = Buffer.from(Math.random().toString()).toString("base64").substring(10,15); const filename = `test_screenshot_upload${randomNumber}.png`; // 先确保连接正常,再执行上传 connectSFTP() .then(() => sftp.put('screenshot.png', `/path/folder/folder/${filename}`)) .then(() => { console.log(`uploaded screenshot to server as ${filename}`); }) .catch(err => { console.log(err, 'catch error'); // 连接出错时标记为未连接,下次尝试重连 isConnected = false; }); } setInterval(uploadScreenshot, 5000); uploadScreenshot();
内容的提问来源于stack exchange,提问作者Genesis1337
相关产品推荐
相关产品推荐

