You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ReactJS+Node网站实现SSH服务器文件传输的方案求助

解决方案:通过Node后端代理实现SSH文件传输

ssh2、node-ssh这类库是Node.js专属模块,依赖Node的核心API(如net、fs),浏览器环境无法提供这些底层能力,所以直接在React前端使用必然报错。正确的做法是搭建Node后端服务,由后端处理SSH连接和文件传输,前端仅负责收集用户输入并向后端发起请求。

具体实现步骤

1. 搭建Node后端服务(以Express为例)

首先创建后端项目,安装所需依赖:

mkdir ssh-file-proxy && cd ssh-file-proxy
npm init -y
npm install express node-ssh cors multer

编写后端服务代码(server.js):

const express = require('express');
const cors = require('cors');
const multer = require('multer');
const { NodeSSH } = require('node-ssh');
const fs = require('fs');
const path = require('path');

const app = express();
app.use(cors());

// 创建临时上传目录
const uploadDir = path.join(__dirname, 'uploads');
if (!fs.existsSync(uploadDir)) {
  fs.mkdirSync(uploadDir);
}

// 配置multer处理文件上传
const upload = multer({ 
  dest: uploadDir,
  limits: { fileSize: 10 * 1024 * 1024 } // 限制文件大小为10MB
});

// 处理SSH文件上传的API接口
app.post('/upload-to-ssh', upload.single('file'), async (req, res) => {
  const { host, username, password, remotePath } = req.body;
  const localFile = req.file;

  if (!localFile) {
    return res.status(400).json({ error: '未选择上传文件' });
  }

  const ssh = new NodeSSH();

  try {
    // 建立SSH连接
    await ssh.connect({
      host,
      username,
      password,
      readyTimeout: 10000 // 连接超时时间10秒
    });

    // 上传文件到远程服务器指定路径
    await ssh.putFile(localFile.path, remotePath);

    // 清理本地临时文件
    fs.unlinkSync(localFile.path);

    ssh.dispose();
    res.status(200).json({ message: '文件上传成功' });
  } catch (err) {
    console.error('SSH操作异常:', err);
    // 异常时也清理临时文件
    if (fs.existsSync(localFile.path)) {
      fs.unlinkSync(localFile.path);
    }
    ssh.dispose();
    res.status(500).json({ error: '文件上传失败', details: err.message });
  }
});

const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
  console.log(`后端服务运行在端口 ${PORT}`);
});

2. 前端React页面实现

在你的React项目中,添加一个文件上传组件,收集用户输入并调用后端API:

import { useState } from 'react';

function SSHUploadForm() {
  const [host, setHost] = useState('');
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [remotePath, setRemotePath] = useState('');
  const [selectedFile, setSelectedFile] = useState(null);
  const [statusMsg, setStatusMsg] = useState('');

  const handleFileSelect = (e) => {
    setSelectedFile(e.target.files[0]);
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    setStatusMsg('');

    if (!selectedFile) {
      setStatusMsg('请选择要上传的文件');
      return;
    }

    const formData = new FormData();
    formData.append('host', host);
    formData.append('username', username);
    formData.append('password', password);
    formData.append('remotePath', remotePath);
    formData.append('file', selectedFile);

    try {
      const response = await fetch('http://localhost:3001/upload-to-ssh', {
        method: 'POST',
        body: formData
      });

      const result = await response.json();
      if (response.ok) {
        setStatusMsg(result.message);
        // 重置表单
        setHost('');
        setUsername('');
        setPassword('');
        setRemotePath('');
        setSelectedFile(null);
      } else {
        setStatusMsg(`上传失败: ${result.details}`);
      }
    } catch (err) {
      setStatusMsg('请求后端服务失败,请检查服务是否启动');
    }
  };

  return (
    <div className="ssh-upload-form">
      <h3>远程SSH文件上传</h3>
      <form onSubmit={handleSubmit}>
        <div className="form-group">
          <label>SSH服务器IP:</label>
          <input
            type="text"
            value={host}
            onChange={(e) => setHost(e.target.value)}
            required
            placeholder="例如: 192.168.1.100"
          />
        </div>
        <div className="form-group">
          <label>用户名:</label>
          <input
            type="text"
            value={username}
            onChange={(e) => setUsername(e.target.value)}
            required
          />
        </div>
        <div className="form-group">
          <label>密码:</label>
          <input
            type="password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            required
          />
        </div>
        <div className="form-group">
          <label>远程文件路径:</label>
          <input
            type="text"
            value={remotePath}
            onChange={(e) => setRemotePath(e.target.value)}
            required
            placeholder="例如: /home/user/docs/upload.pdf"
          />
        </div>
        <div className="form-group">
          <label>选择本地文件:</label>
          <input type="file" onChange={handleFileSelect} required />
        </div>
        <button type="submit">开始上传</button>
      </form>
      {statusMsg && <p className="status-message">{statusMsg}</p>}
    </div>
  );
}

export default SSHUploadForm;

3. 运行与测试

  1. 启动后端服务:node server.js
  2. 启动React前端项目:npm start
  3. 在前端页面填写SSH服务器信息、选择文件后提交,即可完成文件上传

生产环境注意事项

  • 安全加固:使用HTTPS传输数据,避免密码明文暴露;添加用户身份验证,限制API访问权限;对用户输入的远程路径做合法性校验,防止路径遍历攻击。
  • 密钥登录:如果需要使用SSH密钥而非密码登录,可修改后端ssh.connect配置,传入privateKey参数(读取本地密钥文件或接收前端传入的密钥内容)。
  • 错误处理:完善后端的异常捕获逻辑,针对不同错误类型(如连接超时、权限不足、文件不存在)返回更友好的提示信息。

内容的提问来源于stack exchange,提问作者Linir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 12:15:31