Programming is a wonderful mix of art and science; source code is both a poem and a math problem. It should be as simple and elegant as it is functional and fast. This blog is about that (along with whatever else I feel like writing about).

Friday, February 15, 2008

JSch: SCP a File in Java

If you want to upload a file to another computer, SCP is an excellent way to go. And if you want to do it from within a Java program, your best bet is to use the JSch library from JCraft. They've implemented the SSH protocol purely in Java, and it works splendidly. I've written a nice little class that encapsulates the action of sending a file.

Using the class is pretty simple. It goes like this:
FileSender fs = new FileSender("172.16.40.128", "user", "user");
boolean ret = fs.sendFile("/home/sschulte/Desktop/AllPhysics.wmv", "AllPhysics.wmv");
The constructor takes a host, a username, and a password. The sendFile() method takes a source filename and a destination filename. It returns true if the upload was successful, false if it failed.

I developed it in Netbeans, and in order to use JSch, the first step is to download the JAR and add it as a library in your project. (When I first tried it, I downloaded the ZIP instead, and added the source to my project; this works, but it's not necessary to compile this library along with your project unless you plan to edit it. I didn't.)

Most of the code in this class is taken from the ScpTo example included in the ZIP file. I recommend reading it. Something to pay close attention to, however, is the SSH known_hosts file. Their example file doesn't take this into account, so if you just run their code, you get an unknown host exception and it doesn't work. So be sure to include the following:
String knownHostsFilename = "/home/sschulte/.ssh/known_hosts";
jsch.setKnownHosts(knownHostsFilename);
(This is, of course, after you instantiate your JSch object.)

I like it, and it works. But it definitely needs a few things before I'm satisfied.
  • Read the known_hosts filename from a config file, rather than compiling it into the code.
  • Have a config file for the host/username/password information, possibly for multiple hosts at once.
  • Throw exceptions for various types of failures, rather that simply returning false.
Pretty cool stuff. The JCraft guys did a great job with this library.

24 comments:

Anonymous said...

Is this line a typo?
FileSender fs = new FileSender("172.16.40.128", "user", "user");

should it be ..?
FileSender fs = new FileSender("172.16.40.128", "user", "password");

I am looking to use jsch to upload documents to a weblogic server.

Would I need to replace the url with the weblogic url from a properties file?

Would it be possible to also post the sendFile() method?

Thanks
newby

Sean Schulte said...

It's not a typo; the server I'm using is an Ubuntu JEOS VM, where the password is "user". You're going to want to put the URL of your server in there, whether you get it from a properties file or not.

This is an older version of the sendFile() method, without the abstractions and error handling I've since put in, but it should still be pretty helpful:

public boolean sendFile(String sourceFilename, String destFilename) {
FileInputStream fis = null;

try {
connect();

String command = "scp -p -t " + destFilename;
Channel channel = _session.openChannel("exec");
((ChannelExec) channel).setCommand(command);

OutputStream out = channel.getOutputStream();
InputStream in = channel.getInputStream();

channel.connect();

if (checkAck(in) != 0) {
return false;
}

//send "C0644 filesize filename" where filename doesn't contain a /
long filesize = (new File(sourceFilename)).length();
command = "C0644 " + filesize + " ";
if (sourceFilename.lastIndexOf('/') > 0) {
command += sourceFilename.substring(sourceFilename.lastIndexOf('/') + 1);
} else {
command += sourceFilename;
}
command += "\n";

out.write(command.getBytes());
out.flush();

if (checkAck(in) != 0) {
return false;
}

//send the contents of the source file
fis = new FileInputStream(sourceFilename);
byte[] buf = new byte[1024];
while (true) {
int len = fis.read(buf, 0, buf.length);

if (len <= 0) {
break;
}

out.write(buf, 0, len);
}

fis.close();
fis = null;

//send '\0' to end it
buf[0] = 0;
out.write(buf, 0, 1);
out.flush();

if (checkAck(in) != 0) {
return false;
}

out.close();

channel.disconnect();
_session.disconnect();

return true;
} catch (Exception e) {
e.printStackTrace();
try {
if (fis != null) {
fis.close();
}
} catch (Exception ee) {
ee.printStackTrace();
}
}

return false;
}

Enjoy.

Anonymous said...

Thanks for posting the code. Is SCP the same or similar to SFTP, which is secure FTP? I have seen jscp used to sftp files over.

Anonymous said...

Hi Sean,

I am in the process of implementing JSch: SCP a File in Java, but have not had much success. It is most likely because I have not coded the SSH known_hosts correction you mentioned. This is because I did not find a known_hosts file on our server. Do I need to install SSH on the server in order to get a known_hosts file? Please advise.

Thanks, Newby.

Jason Whaley said...

I was googling around and happened upon your blog post. I had some spare time and quickly wrote a wrapper class for scp'ing files with jsch. I don't have it packaged into a jar yet, but you are welcome to check it out from my google code repository and build it yourself.

Here's the project page: http://code.google.com/p/securechannelfacade/

I'm using maven so building should be pretty straightforward... `mvn clean install`

Anonymous said...

I was able to get it to work without setting known hosts by specifying session.setConfig("StrictHostKeyChecking", "no")

Anonymous said...

So how do I specify which directory to scp to/from if I can't use a '/' character in the C0644 command?

Anonymous said...

Hey Sean can you share your java code.. I would like to try out your class for using JSch... please let me know where can I download it from.

Anonymous said...

Hi Seam,
I am in need of a java program which will transfer a file from one pc to another. Where do i start? Sorry to ask...But i couldn't get the right way.

Please guide me...
Rakesh.
mehra.16@gmail.com

Anonymous said...

I have successfully implemented the Jsch code on the server and able to upload a file from server to another computer, but when i try to upload the file from other computer by giving the server ip address as url in address bar(say http://192.168.1.54:8084/upload), the upload is not successful. It show an FileNotFoundException.



Thanks,
ethan
ethanbroad@rocketmail.com

CT said...

Hi Sean
I am using the latest jar file jsch-0.1.42.jar from jcraft. I am having a wiered issues ,sometimes the file copied to the remote computer has only zero bytes. I am using the same code what you are using except little diffeence wheer you are calling the checkAck method and am not. Here is my code. My question is

1.) what is the importance of the code

if (checkAck(in) != 0) {
return false;
}

should I include in my send file method.

2.) How can I check the size of the transferred file.
I would highly appreciate,
If you can throw soem light on this .

Thank you
CT

JKlass said...

Where can i get this code?

Anonymous said...

I tried the same. No error occurs but the file isnt uploaded in remote location. Following is my code:

public boolean sendFile(String sourceFilename, String destFilename)
{
FileInputStream fis = null;
Session session = null;

try
{
JSch jsch = new JSch();
//jsch.setKnownHosts("D:/sftpRoot/test");

String knownHostsFilename = "/home/sschulte/.ssh/known_hosts";
jsch.setKnownHosts(knownHostsFilename);

// Create session
session = jsch.getSession(this.user, this.host, 50000);

UserInfo userInfo = new MyUserInfo();
((MyUserInfo)userInfo).setPassword(this.password);

session.setUserInfo(userInfo);
session.setPassword(this.password);

// connect
session.connect();

String command = "scp -p -t " + destFilename;
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);

OutputStream out = channel.getOutputStream();
//InputStream in = channel.getInputStream();

channel.connect();

//send "C0644 filesize filename" where filename doesn't contain a /
long filesize = (new File(sourceFilename)).length();
command = "C0644 " + filesize + " ";
if (sourceFilename.lastIndexOf('/') > 0)
{
command += sourceFilename.substring(sourceFilename.lastIndexOf('/') + 1);
}
else
{
command += sourceFilename;
}

command += "\n";

out.write(command.getBytes());
out.flush();

//send the contents of the source file
fis = new FileInputStream(sourceFilename);
byte[] buf = new byte[1024];
while (true)
{
int len = fis.read(buf, 0, buf.length);

if (len <= 0)
{
break;
}

out.write(buf, 0, len);
}

fis.close();
fis = null;

//send '\0' to end it
buf[0] = 0;
out.write(buf, 0, 1);
out.flush();

out.close();

channel.disconnect();
session.disconnect();

return true;
}
catch (Exception e)
{
e.printStackTrace();
try
{
if (fis != null)
{
fis.close();
}
}
catch (Exception ee)
{
ee.printStackTrace();
}
}

return false;
}

public static class MyUserInfo implements UserInfo
{
public String password = null;


/**
*
* @param password
*/
public void setPassword(String password)
{
this.password = password;
}



public String getPassword()
{
return this.password;
}

public boolean promptYesNo(String str)
{
return true;
}

public String getPassphrase()
{
return null;
}

public boolean promptPassphrase(String message)
{
return true;
}

public void showMessage(String message)
{

}

public boolean promptPassword(String message)
{
return true;
}
}

public static void main(String[] args)
{
FileUploader fileUploader = new FileUploader("192.168.33.82", "test", "test");
boolean uploadStatus = fileUploader.sendFile("D:/test111.txt", "test111.txt");
System.out.println("File Upload successful: "+uploadStatus);
}

Brinkster said...

Hi
Same question as JKlassMoney had, where can I get this class?

Anonymous said...

Good effort mate. But you can simply use JSch's built in ChannelSftp class. It does all the heavy lifting for you.

Anonymous said...

For a full integrated solution with JFileUpload.
http://www.jfileupload.com/products/scp/index.html

Michel Gentile said...

Hello Sean, can you share your java class ? I have to automatically upload files via SCP and so I am very interested in your code... please let me know where can I download it from.

Unknown said...

how can i get this java class? Very needed

Maya said...

I used the same code but copying failed if I try to copy to a new directory calculated during prog execution which doesnt exist. How to create a new directory and then scp the file from source to it?

Anonymous said...

I" using an Ant script for SCP.
You can find the tutorial at

http://sshadmincontrol.com/jsch-scp-files-from-multiple-hosts-using-apache-ant-task

Anonymous said...

Try using the java code from

http://sshadmincontrol.com/sftp-server-communication-in-java-using-jsch

Prakash said...
This comment has been removed by the author.
Prakash said...

I have tried it with multiple clients at the same time.At some time it is giving some error as

"Exception in thread "Exec thread 223.235.25.115" java.lang.NullPointerException
at com.jcraft.jsch.ChannelSession.run(Unknown Source)
at java.lang.Thread.run(Thread.java:662)

Kindly advice me to solve the issue..

Anonymous said...

Am not able to see FileSender class in jsch-0.1.50.jar.

Could you please tell any other option to file upload using jsch__.jar