如何使用Java SFTP库JSch将文件从一个目录传输到另一个目录?

18

我需要使用JSch库来编写文件传输程序。 我有一个简单的目录,其中包含两个文件夹 -

enter image description here

SFTP_1文件夹中,我有一张位图图像。 SFTP_2文件夹只是一个空文件夹。 我的目标是使用SFTP将图像从SFTP_1传输到SFTP_2。

这是我的代码:

FileTransfer fileTransfer = new FileTransfer();              
      
JSch jsch = new JSch();

String host = "127.0.0.1";
int port = 22;

String user = "user";
Session session = jsch.getSession(user, host, port);      
session = jsch.getSession("username", "127.0.0.1", 22);
session.connect();

ChannelSftp sftp = null;
sftp = (ChannelSftp)session.openChannel("sftp") ; //channel;

sftp.rename(
    "C:\\Users\\ADMIN\\Desktop\\Work\\ConnectOne_Bancorp\\Java_Work\\SFTP_1\\house.bmp",
    "C:\\Users\\ADMIN\\Desktop\\Work\\ConnectOne_Bancorp\\Java_Work\\SFTP_2\\house.bmp");
session.disconnect();

我想做的只是将一个文件从我的电脑中的一个目录转移到另一个目录。欢迎任何提示,谢谢!

4个回答

13

请注意,在两个文件夹之间复制时,无需使用SFTP协议。可以在不涉及远程文件传输协议的情况下从一个文件夹复制到另一个文件夹,SFTP主要用于远程复制文件,从本地机器复制到远程机器,或从远程机器复制到(同一或不同的)远程机器,或者从远程机器到本地机器。

这是因为FTP是一种基于网络的协议。因此,使用它(或任何相关协议)将使用网络(或模拟网络)。

JSch提供的安全性旨在保护免受网络上发生的某些攻击。它不会在计算机内部提供任何额外的安全保障。

要在单台计算机的文件夹之间复制文件,最简单的方法是不使用JSch,如下所示:

private static void copyFileUsingJava7Files(File source, File dest)
        throws IOException {
    Files.copy(source.toPath(), dest.toPath());
}

还有其他技巧,如果你真的想使用JSch,你需要意识到JSch必须提供大量的“额外”信息才能连接到你所在的机器,因为它会尝试像从网络上连接一样连接到这台机器。

Session sessionRead = jsch.getSession("username", "127.0.0.1", 22);
sessionRead.connect();

Session sessionWrite = jsch.getSession("username", "127.0.0.1", 22);
sessionWrite.connect();

ChannelSftp channelRead = (ChannelSftp)sessionRead.openChannel("sftp");
channelRead.connect();

ChannelSftp channelWrite = (ChannelSftp)sessionWrite.openChannel("sftp");
channelWrite.connect();

PipedInputStream pin = new PipedInputStream(2048);
PipedOutputStream pout = new PipedOutputStream(pin);

channelRead.get("/path/to/your/file/including/filename.txt", pout);
channelWrite.put(pin, "/path/to/your/file/destination/including/filename.txt");

channelRead.disconnect();
channelWrite.disconnect();

sessionRead.disconnect();
sessionWrite.disconnect();

以上代码缺乏错误检查、异常处理和回退例程,如果文件缺失、网络未连接等等情况出现,该程序将无法正常运行。但是你应该能够理解主要思路。

显然,使用不需要的网络协议会打开更多的故障场景。只有在你的程序需要复制不仅位于你的机器上的文件时,才使用SFTP方法。


1
虽然这确实可以工作,但我认为您不需要两个SSH会话。只需通过单个SSH连接使用两个SFTP通道即可。一般来说,甚至一个单独的SFTP通道就足够了。SFTP协议可以并行处理多个文件(读取一个,写入另一个)。但我不确定JSch SFTP实现是否支持并行文件。 - Martin Prikryl
1
@MartinPrikryl 你可能是正确的,一个会话可能是可用的。我使用了两个,主要是因为将来,我希望会话迁移到不同的帐户和机器上。我知道问题的提出是这样的,即迁移它们离开相同的帐户和机器是“确保不会发生的”,但由于他正在寻找网络协议,很可能有人计划移动这些东西,而他没有被告知全部情况(比如我们只有一台机器来开发它,但以后我们会有三台)。 - Edwin Buck
值得解释的是,为什么需要下载和重新上传文件来复制它,而不是在服务器上直接进行原地复制。此外还有其他选项,比如使用cp shell命令(如果有shell访问权限的话)。请参见在Java中使用SFTP,如何将文件从一个文件夹传输到另一个文件夹? - Martin Prikryl
@MartinPrikryl 我不确定这样的问题应该在哪里提出。毕竟,它不是“如何做”的答案的一部分;而且,答案可能就像“我不知道为什么,但我被告知这是一个不可谈判的要求,必须这样做”。同样,可能有一个合理的非废话的原因,就像你一样,我也看不到这样的原因。无论哪种方式,我都不想让他回答“为什么?”的问题,因为这似乎只是一个邀请,告诉他“你没有得到答案,因为你需要做其他事情!” :) - Edwin Buck

3

实际上,JSch是为远程工作而设计的,文件系统修改是这种工作类型之一。@ Edwin Buck的答案使用网络在远程主机上的本地文件夹之间进行复制。有更好的方法:

session.connect();
ChannelExec exec = (ChannelExec) session.openChannel("exec");
exec.setCommand("cp a.out b.out");
exec.connect();

我手头没有Windows,所以我为Unix准备了样本。但是这个想法很简单:在远程主机上执行复制命令。

这个问题涉及到SFTP。但这不是SFTP的解决方案。它依赖于shell访问,并且与平台有关。它只能在*nix服务器上运行。 - Martin Prikryl
SFTP 是通过 SSH 进行文件传输。虽然可以配置 SSH 服务并禁止执行,但在现实生活中并不常见。此示例也适用于 Windows 服务器。您需要使用复制命令而不是 cp 命令。 - sibnick
"你需要使用复制命令而不是cp命令":这就是我所说的依赖平台的解决方案。而且,仅限SFTP访问并不罕见。特别是当你连接到外部(例如客户或供应商)服务器时,通常只有SFTP访问权限,没有shell。 - Martin Prikryl
实际上,这与平台有关:区分大小写或不区分大小写,平台特定的路径分隔符。但是,如果您可以在本地执行复制操作,则应在本地执行。 - sibnick
不,SFTP始终使用/。无论远程端是什么平台。如果您始终将文件视为区分大小写,则大小写敏感性实际上并不是问题。 - Martin Prikryl
你关于 / 的理解是正确的,但允许使用的符号和编码方式,以及文件名长度的最大值也因平台而异,因此在远程文件系统上进行任何操作都是与平台相关的。位图图像可能非常大,如果可以在本地执行操作,应该尽量在本地执行。 - sibnick

2
如果原帖作者真正寻找JSch在两个不同FTP站点之间实际操作的工作示例,请参考以下内容:
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
...
JSch jsch = new JSch();
JSch session = null;
try {
  session = jsch.getSession(userid, sourceservername, sourceserverport);
  session.setPassword(sourceserverpassword);
  Properties props = new Properties();
  props.put("StrictHostKeyChecking", "no");
  session.setConfig(props);
  session.connect();
  Channel channel = session.openChannel("sftp");
  channel.connect();
  ChanelSftp channelsftp = (ChannelSftp) channel;
  channelsftp.cd(sourcefilepath);
  channelsftp.lcd(localfilepath);
  FileOutputStream fos = new FileOutputStream(new File(localfilepath + "/" + localfilename));
  channelsftp.get(sourcefilename, fos);
  fos.flush();
  fos.close();
  channelsftp.disconnect()
  session.disconnect();
} catch (Exception e) {
  e.printStackTrace();
}

实际上,您可以将这些操作拆分为单独的try{}catch(){}块语句,以引入更细粒度的错误报告,并添加任何信息输出行以通知用户状态等。但这样做可以使您达到目的。尽管JSch示例比大多数免费库的示例都要好,即使像这个好的示例也可能存在一些遗漏,这些遗漏可能会破坏您尝试使它们正常工作的努力。如果这不是原始帖子的作者,那么希望这可以帮助其他正在寻找可用的JSch示例的人。一旦您使它正常工作,它就会像魔法一样运行,所以这值得花点时间。

0

核心的SFTP协议不支持复制远程文件。

有一个copy-file扩展协议的草案,但只有少数SFTP服务器支持(例如ProFTPD mod_sftp和Bitvise SFTP服务器)。

在最广泛使用的OpenSSH SFTP服务器中,仅在最近的9.0版本中支持。

而且JSch库也不支持它。


请参考我的回答如何使用SFTP将文件复制/复制到另一个目录?


所以,实际上使用 cp shell 命令通过 "exec" 通道 (ChannelExec) 是目前可用的最佳方法(假设您连接到一个 *nix 服务器并且具有 shell 访问权限)。


如果您没有shell访问权限,那么您唯一的选择确实是将文件下载到本地临时文件夹并将其上传回新位置(或使用流来避免使用临时文件)。这就是@Edwin Buck的被接受的答案所展示的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接