Monday, July 23, 2007
Accessing files over SFTP in Java
We have a lot of data files that needs to be copied to each of the machine we want to run our code on. Further when these data files are updated, they need to be updated on all the machines. This means that the developer has to spend a lot of time just copying these data files around. I wrote a simple solution to this where latest versions of all the data files are maintained at a central server accessible via ssh. These remote date files are copied to the local machine when required in an on-demand fashion transparently by the Java program (after comparing last modification times of the local and remote file).
For accessing files over SFTP, we are using Apache Commons VFS along with Jsch. These libraries (especially commons VFS) is not well documented. I am therefore posting some code snippets from our code documenting the API
The first code snippet demonstrates the API for copying a file from remote location to the local machine:
/**
* Copies a remote file to local filesystem.
*/
public static void copyRemoteFile(String host, String user,
String password, String remotePath, String localPath) throws IOException {
// we first set strict key checking off
FileSystemOptions fsOptions = new FileSystemOptions();
SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(
fsOptions, "no");
// now we create a new filesystem manager
DefaultFileSystemManager fsManager = (DefaultFileSystemManager) VFS
.getManager();
// the url is of form sftp://user:pass@host/remotepath/
String uri = "sftp://" + user + ":" + password + "@" + host
+ "/" + remotePath;
// get file object representing the local file
FileObject fo = fsManager.resolveFile(uri, fsOptions);
// open input stream from the remote file
BufferedInputStream is = new BufferedInputStream(fo.getContent()
.getInputStream());
// open output stream to local file
OutputStream os = new BufferedOutputStream(new FileOutputStream(
localPath));
int c;
// do copying
while ((c = is.read()) != -1) {
os.write(c);
}
os.close();
is.close();
// close the file object
fo.close();
// NOTE: if you close the file system manager, you won't be able to
// use VFS again in the same VM. If you wish to copy multiple files,
// make the fsManager static, initialize it once, and close just
// before exiting the process.
fsManager.close();
System.out.println("Finished copying the file");
}
Unfortunately the Commons VFS api does not provide a way to check last modification time of a remote file. I had to write that code using the Jsch API. Below is a code snippet that returns last modification time in seconds:
/**
* Returns a Sftp session conncted using the Jsch library.
*/
public static Session connectSFTP(final String host, final String user,
final String pass) throws JSchException {
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, 22);
session.setUserInfo(new UserInfo() {
public String getPassphrase() {
return null;
}
public String getPassword() {
return null;
}
public boolean promptPassphrase(String string) {
return false;
}
public boolean promptPassword(String string) {
return false;
}
public boolean promptYesNo(String string) {
return true;
}
public void showMessage(String string) {
}
});
session.setPassword(pass);
session.connect();
return session;
}
/**
* Returns last modification time of a remote file in seconds.
*/
public static int getLastModificationTime(String host, String user,
String password, String remotePath) throws IOException,
JSchException, SftpException {
Session session = connectSFTP(host, user, password);
ChannelSftp chan = (ChannelSftp) session.openChannel("sftp");
chan.connect();
SftpATTRS attrs = chan.lstat(remotePath);
int time = attrs.getMTime();
chan.disconnect();
session.disconnect();
return time;
}
I hope that this code is useful to others. Please leave a comment if you see any error of have a suggestion.
ujjal
Is there a way to share this code, and comments?
thanks
http://www.enterprisedt.com/products/edtftpjssl/overview.html
Again, it is not open source but it includes neat features like connection pooling and asynchronous operations.
Also supports FTP and FTPS. We do have a very popular open source FTP library too which this is built on - edtFTPj.
// get file object representing the local file
but it should really read
// get file object representing the remote file
Thank you for the code!
http://people.apache.org/builds/commons/nightly/commons-vfs/
public static final void copyLocalFileToRemote(String host, String user, String password, String remotePath, String localPath) {
FileObject fo = null;
BufferedInputStream is = null;
OutputStream os = null;
// we first set strict key checking off
FileSystemOptions fsOptions = new FileSystemOptions();
try{
SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(fsOptions, "no");
// now we create a new filesystem manager
DefaultFileSystemManager fsManager = (DefaultFileSystemManager) VFS.getManager();
// the url is of form sftp://user:pass@host/remotepath/
String uri = "sftp://" + user + ":" + password + "@" + host + "/" + remotePath;
// get file object representing the remote file
fo = fsManager.resolveFile(uri, fsOptions);
// open input stream from the local file
is = new BufferedInputStream(new FileInputStream(localPath));
// open output stream to remote file
os = new BufferedOutputStream((fo.getContent().getOutputStream()));
int c;
// do copying
while ((c = is.read()) != -1) {
os.write(c);
}
os.close();
is.close();
// close the file object
fo.close();
}catch(IOException ex){
System.out.println("SEVERE (convert to log4j) Error -- Caught IOException: ");
ex.printStackTrace();
}finally{
/* No need to worry about empty catch blocks, just cleaning up incase */
try{
if(fo != null)
fo.close();
}catch(Exception ex){}
try{
if(is != null)
is.close();
}catch(Exception ex){}
try{
if(os != null)
os.close();
}catch(Exception ex){}
}
}
Can you please give an eg of JSchOverJHttpTunnel
: Invalid URI escape sequence "%ph".
org.apache.commons.vfs2.FileSystemException: Invalid URI escape sequence "%gf".
at org.apache.commons.vfs2.provider.UriParser.decode(UriParser.java:343)
at org.apache.commons.vfs2.provider.UriParser.decode(UriParser.java:308)
at org.apache.commons.vfs2.provider.UriParser.checkUriEncoding(UriParser.java:462)
at org.apache.commons.vfs2.impl.DefaultFileSystemManager.resolveFile(DefaultFileSystemManager.java:678)
at org.apache.commons.vfs2.impl.DefaultFileSystemManager.resolveFile(DefaultFileSystemManager.java:621)
<< Home