18

I need to program a file transfer using JSch library. I have a simple directory with two folders -

enter image description here

In the SFTP_1 folder, I have a bitmap image. And the SFTP_2 folder is just an empty folder. My goal is to transfer the image using SFTP from SFTP_1 to SFTP_2 .

Here is my code thus far :

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();

What I would like to do is to simply transfer a file from one directory in my machine, to another directory. any tips appreciated, thanks !

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Caffeinated
  • 11,982
  • 40
  • 122
  • 216

4 Answers4

13

Note that to copy between two folders, one doesn't need to use SFTP. One can copy from one folder to another without involving the SFTP protocol which is primarly used to copy files remotely, either from the local machine to a remote machine, or from a remote machine to (the same or a different) remote machine, or from the remote machine to the local machine.

That's because the FTP is a network based protocol. So using it (or any of it's related protocols) is going to use the network (or a simulated network).

The security that JSch provides is security designed to protect from certain kinds of attacks that occur on networks. It will not provide any extra security within the machine.

To copy files between folders on a single machine, the simplest way to do so is not to use JSch, like so

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

There are other techniques, and if you really want to use JSch, you need to realize that JSch must be provided a lot of "extra" information to connect to the machine you are on, because it will try to connect to this machine as if it were connecting from across the network

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();

The above code lacks error checking, exception handling, and fall back routines for if files are missing, networks are not up, etc. But you should get the main idea.

It should also be obvious that using a network protocol where no network protocol needs to exist opens the door to a lot more failure scenarios. Only use the SFTP method if your program is soon meant to copy files that are not both located on your machine.

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
  • 1
    While this will indeed work, I do not think you need two SSH sessions. It should work with just two SFTP channels over a single SSH connection. In general even a single SFTP channel should be enough. The SFTP protocol can work with multiple files in parallel (reading one, writing another). But I'm not sure, if JSch SFTP implementation is built to support parallel files. – Martin Prikryl Sep 29 '15 at 15:39
  • 1
    @MartinPrikryl You're likely to be correct, one session might be usable. I used two, primarily because in the future, I would expect the sessions to migrate away from the same account and machine. I know that the question is posed such that migrating them away from the same account and machine are _assured_ to not happen, but since he's reaching for a network protocol, it's likely that someone plans to move these things, and he's not being told the full picture (like we only have one machine to develop it on, but later we'll have three). – Edwin Buck Sep 29 '15 at 16:02
  • It might also be worth explaining, why it's actually needed to download and re-upload a file to duplicate it, instead of duplicating it in-place on the server. Also there are other options, like using `cp` shell command (if one has a shell access). See [Using SFTP in Java, How do I transfer a file from one folder to another?](https://stackoverflow.com/q/32742066/850848) – Martin Prikryl Nov 02 '18 at 08:19
  • @MartinPrikryl I'm not sure where such a question would be posed. After all, it's not part of the answer of "how to do it"; and, the answer might be as simple as "I don't know why, but I've been told it is a non-negotiable requirement to do it this way". Again, there might be a reasonable non-nonsense reason why, and like you, I don't see such a reason. Either way, I wouldn't want to him answering the question of "Why?" because it seems nothing more than an invitation to be told "you don't get an answer, because you need to do something else!" :) – Edwin Buck Nov 02 '18 at 22:17
3

Actually JSch is designed for remote work, and file system modification is one of the type such work. @Edwin Buck answer uses network for coping between local folders on remote host. There is better approach:

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

I have no windows on the hand, as result my sample is for unix. But the idea is simple: execute copy command on the remote host.

sibnick
  • 3,995
  • 20
  • 20
  • The question is about SFTP. This is not SFTP solution. It relies on a shell access. And it's platform-dependent. It will work on *nix servers only. – Martin Prikryl Sep 29 '15 at 15:35
  • SFTP is file transfer over SSH. It is possible configure SSH service and forbidden exec, but it is not very often in real life. This sample shouls work on windows server too. You need use copy command instead of cp – sibnick Sep 29 '15 at 15:49
  • *"You need use copy command instead of cp"*: That what I call platform-dependent solution. And SFTP-only access is not that rare. Particularly if you are connecting to an external (e.g. customer's or supplier's) server, you typically have SFTP access only, no shell. – Martin Prikryl Sep 29 '15 at 16:02
  • Actually it is platfrom specific anyway: case sentitive or not, platform specific path separator. But if you may perform copy locally you should do it locally. – sibnick Sep 29 '15 at 16:37
  • No, SFTP always uses `/`. No matter what platform is on remote side. Case sensitivity is not really an issue, if you always treat the files as case sensitive. – Martin Prikryl Sep 29 '15 at 16:40
  • You are correct about `/`, but allowed symbols and encoding, max file name length are platform specific too, thus any work on remote file system is platform specific. Bitmap image may be big enough and again if you can perform operation locally you should do it locally. – sibnick Sep 29 '15 at 16:56
2

If the original poster is actually looking for a working example of JSch in action between two distinct FTP sites, here goes:

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();
}

In practice you might break up these actions into separate try{}catch(){} blocked statements so as to introduce more granular error reporting, as well as add any informational output lines to inform the user of status, etc. But this'll get you there. Admittedly while the JSch examples are better than most such examples from freeware libraries, even good ones like this one, there can be some omissions among them around details that can make or break your attempt to get the things to work. Hope this helps if not the original poster, then someone else looking for a working JSch example. Once you have it working, it does go like a charm, so it's worth the trouble.

Matt Campbell
  • 1,967
  • 1
  • 22
  • 34
0

A core SFTP protocol does not support duplicating remote files.

There's a draft of copy-file extension to the protocol, but that's supported by only few SFTP servers (ProFTPD mod_sftp and Bitvise SFTP server for example).

In the most widespread OpenSSH SFTP server it is supported only by very recent version 9.0.

And it's also not supported by the JSch library.


See also my answer to How can I copy/duplicate a file to another directory using SFTP?


So actually using the cp shell command over an "exec" channel (ChannelExec) is unfortunately the best available approach (assuming you connect to a *nix server and you have a shell access).


If you do not have a shell access, then your only option is indeed to download the file to a local temporary folder and upload it back to the new location (or use streams, to avoid a temporary file). This is what the accepted answer by @Edwin Buck shows.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992