perf: 上传到主机支持scp方式

This commit is contained in:
xiaojunnuo
2025-03-24 23:45:45 +08:00
parent c56f48c1e3
commit 05b6159802
6 changed files with 1438 additions and 27 deletions

View File

@@ -7,6 +7,8 @@ import { SshAccess } from "./ssh-access.js";
import stripAnsi from "strip-ansi";
import { SocksClient } from "socks";
import { SocksProxy, SocksProxyType } from "socks/typings/common/constants.js";
import { ScpClient } from "../scp/index.js";
import fs from "fs";
export type TransportItem = { localPath: string; remotePath: string };
export class AsyncSsh2Client {
@@ -265,15 +267,15 @@ export class SshClient {
}
* @param options
*/
async uploadFiles(options: { connectConf: SshAccess; transports: TransportItem[]; mkdirs: boolean; opts?: { mode?: string } }) {
async uploadFiles(options: { connectConf: SshAccess; transports: TransportItem[]; mkdirs: boolean; opts?: { mode?: string }; uploadType?: string }) {
const { connectConf, transports, mkdirs, opts } = options;
await this._call({
connectConf,
callable: async (conn: AsyncSsh2Client) => {
const sftp = await conn.getSftp();
this.logger.info("开始上传");
for (const transport of transports) {
if (mkdirs !== false) {
if (mkdirs !== false) {
this.logger.info("初始化父目录");
for (const transport of transports) {
const filePath = path.dirname(transport.remotePath);
let mkdirCmd = `mkdir -p ${filePath} `;
if (conn.windows) {
@@ -291,13 +293,60 @@ export class SshClient {
}
await conn.exec(mkdirCmd);
}
await conn.fastPut({ sftp, ...transport, opts });
}
if (options.uploadType === "sftp") {
const sftp = await conn.getSftp();
for (const transport of transports) {
await conn.fastPut({ sftp, ...transport, opts });
}
} else {
//scp
for (const transport of transports) {
await this.scpUpload({ conn, ...transport, opts });
}
}
this.logger.info("文件全部上传成功");
},
});
}
async scpUpload(options: { conn: any; localPath: string; remotePath: string; opts?: { mode?: string } }) {
const { conn, localPath, remotePath, opts } = options;
return new Promise((resolve, reject) => {
// 关键步骤:构造 SCP 命令
try {
this.logger.info(`开始上传:${localPath} => ${remotePath}`);
conn.conn.exec(
`scp -t ${remotePath}`, // -t 表示目标模式
(err, stream) => {
if (err) {
return reject(err);
}
// 准备 SCP 协议头
const fileStats = fs.statSync(localPath);
const fileName = path.basename(localPath);
// SCP 协议格式C[权限] [文件大小] [文件名]\n
stream.write(`C0644 ${fileStats.size} ${fileName}\n`);
// 通过管道传输文件
fs.createReadStream(localPath)
.pipe(stream)
.on("finish", () => {
this.logger.info(`上传文件成功:${localPath} => ${remotePath}`);
resolve(true);
})
.on("error", reject);
}
);
} catch (e) {
reject(e);
}
});
}
async removeFiles(opts: { connectConf: SshAccess; files: string[] }) {
const { connectConf, files } = opts;
await this._call({