如何使用 Deno 通过 SSH 传输文件?

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

我正在寻找一种使用 Deno 通过 SSH 传输文件的方法。我并不是试图允许用户通过网站上传文件,而是想使用 Deno 作为脚本语言将文件上传到服务器,类似于

scp
pscp
。不幸的是,这些都没有在任何 Deno 包装器中使用,所以我想知道如果我想保持交叉兼容性,最好最快的解决方案是什么?

ssh deno
2个回答
3
投票

创建包装器比您想象的要简单:您可以使用 subprocess API 创建对

scp
pscp
的调用,并且可以使用
Deno.build.os
区分平台环境。将它们结合起来实现您的目标非常简单:

./scp.ts

const decoder = new TextDecoder();

export type ProcessOutput = {
  status: Deno.ProcessStatus;
  stderr: string;
  stdout: string;
};

/**
 * Convenience wrapper around subprocess API.
 * Requires permission `--allow-run`.
 */
export async function getProcessOutput(cmd: string[]): Promise<ProcessOutput> {
  const process = Deno.run({ cmd, stderr: "piped", stdout: "piped" });

  const [status, stderr, stdout] = await Promise.all([
    process.status(),
    decoder.decode(await process.stderrOutput()),
    decoder.decode(await process.output()),
  ]);

  process.close();
  return { status, stderr, stdout };
}

// Add any config options you want to use here
// (e.g. maybe a config instead of username/host)
// The point is that you decide the API:
export type TransferOptions = {
  sourcePath: string;
  host: string;
  username: string;
  destPath: string;
};

export function createTransferArgs(options: TransferOptions): string[] {
  const isWindows = Deno.build.os === "windows";
  const processName = isWindows ? "pscp" : "scp";
  const platformArgs: string[] = [processName];

  // Construct your process args here using your options,
  // handling any platform variations:
  if (isWindows) {
    // Translate to pscp args here...
  } else {
    // Translate to scp args here...
    // example:
    platformArgs.push(options.sourcePath);
    platformArgs.push(
      `${options.username}@${options.host}:${options.destPath}`,
    );
  }

  return platformArgs;
}

./main.ts

import * as path from "https://deno.land/[email protected]/path/mod.ts";

import {
  createTransferArgs,
  getProcessOutput,
  type TransferOptions,
} from "./scp.ts";

// locally (relative to CWD): ./data/example.json (or on Windows: .\data\example.json)
const fileName = "example.json";
const sourcePath = path.join(Deno.cwd(), "data", fileName);
// on remote (uses *nix FS paths): /repo/example.json
const destPath = path.posix.join("/", "repo", fileName);

const options: TransferOptions = {
  sourcePath,
  host: "server.local",
  username: "user1",
  destPath,
};

const transferArgs = createTransferArgs(options);
const { status: { success }, stderr, stdout } = await getProcessOutput(
  transferArgs,
);

if (!success) {
  // something went wrong, do something with stderr if you want
  console.error(stderr);
  Deno.exit(1);
}

// else continue...
console.log(stdout);
Deno.exit(0);


0
投票

在 Deno 2 中,子流程 API 已更改(请参阅迁移指南),需要将

Deno.run
替换为
new Deno.Command
。使用 @jsejcksn 的答案 答案,更新后的 Deno 2 版本将是:

./scp.ts

const decoder = new TextDecoder();

export type ProcessOutput = {
  success: boolean;
  stderr: string;
  stdout: string;
};

/**
 * Convenience wrapper around subprocess API.
 * Requires permission `--allow-run`.
 */
export async function getProcessOutput(
  cmd: string[],
): Promise<ProcessOutput> {
  const command = new Deno.Command(Deno.execPath(), {
    args: cmd,
    stderr: "piped",
    stdout: "piped",
  });

  const result = await command.output();

  const success = result.success;
  const stderr = decoder.decode(result.stderr);
  const stdout = decoder.decode(result.stdout);

  return { success, stderr, stdout };
}

// Add any config options you want to use here
// (e.g. maybe a config instead of username/host)
// The point is that you decide the API:
export type TransferOptions = {
  sourcePath: string;
  host: string;
  username: string;
  destPath: string;
};

export function createTransferArgs(options: TransferOptions): string[] {
  const isWindows = Deno.build.os === "windows";
  const processName = isWindows ? "pscp" : "scp";
  const platformArgs: string[] = [processName];

  // Construct your process args here using your options,
  // handling any platform variations:
  if (isWindows) {
    // Translate to pscp args here...
  } else {
    // Translate to scp args here...
    // example:
    platformArgs.push(options.sourcePath);
    platformArgs.push(
      `${options.username}@${options.host}:${options.destPath}`,
    );
  }

  return platformArgs;
}

./main.ts

import {
  createTransferArgs,
  getProcessOutput,
  type TransferOptions,
} from "./scp.ts";

// locally (relative to CWD): ./data/example.json (or on Windows: .\data\example.json)
const fileName = "example.json";
const sourcePath = path.join(Deno.cwd(), "data", fileName);
// on remote (uses *nix FS paths): /repo/example.json
const destPath = path.posix.join("/", "repo", fileName);

const options: TransferOptions = {
  sourcePath,
  host: "server.local",
  username: "user1",
  destPath,
};

const transferArgs = createTransferArgs(options);
const { success, stderr, stdout } = await getProcessOutput(
  transferArgs,
);

if (!success) {
  // something went wrong, do something with stderr if you want
  console.error(stderr);
  Deno.exit(1);
}

// else continue...
console.log(stdout);
Deno.exit(0);

p.s.:我稍微更改了

ProcessOutput
,不返回
status
对象,而只返回
success
变量。

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