Tuesday, January 24, 2012

Commons VFS: SFTP from Java the simple way

I have always used jsch library to SCP (or what is commonly known as SFTP) files in remote servers from Java.

I was tempted to use my old code when I decide to look around for something cleaner just to come across this great post about sftp using Apache VFS. I made few changes to be able to sftp any input stream as a file to a remote SFTP site which I am sharing below.

Dependencies:

                org.apache.commons
                commons-vfs2
                2.0
            
            
                com.jcraft
                jsch
                0.1.45
            

The upload:

package com.nestorurquiza.utils;

package com.nestorurquiza.utils;

import java.io.InputStream;
import java.io.OutputStream;

import org.apache.commons.io.IOUtils;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.commons.vfs2.FileSystemOptions;
import org.apache.commons.vfs2.Selectors;
import org.apache.commons.vfs2.impl.StandardFileSystemManager;
import org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * All credits for http://www.memorylack.com/2011/06/apache-commons-vfs-for-sftp.html
 * 
 * nestoru - 2012/01/24: Small changes:
 *   1. Using logging instead of System.out.println()
 *   2. The initial directory is not the user home directory 
 *   3. No additional slashes when adding remoteFilePath.
 *   4. Using InputStream instead of path to a local file
 * 
 * If you need extra functionality check out the url above
 * 
 * 
 *
 */
public class SftpUtils {
    private final static Logger log = LoggerFactory.getLogger(SftpUtils.class);
    
    public static void upload(String hostName, String username,
            String password, InputStream localInputStream, String localInputStreamName, String remoteFilePath) {
        
        StandardFileSystemManager manager = new StandardFileSystemManager();

        try {
            manager.init();

            FileSystemOptions fileSystemOptions = createDefaultOptions();
            
            // Create local file object
            //FileObject localFile = manager.resolveFile(f.getAbsolutePath());
            FileObject localFile = manager.resolveFile("ram://path/needed/" + localInputStreamName);
            localFile.createFile();
            OutputStream localOutputStream = localFile.getContent().getOutputStream();
            IOUtils.copy(localInputStream, localOutputStream);
            localOutputStream.flush();
            
            // Create remote file object
            FileObject remoteFile = manager.resolveFile(
                    createConnectionString(hostName, username, password,
                            remoteFilePath), fileSystemOptions);

            // Copy local file to sftp server
            remoteFile.copyFrom(localFile, Selectors.SELECT_SELF);

            log.debug("File upload success");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            manager.close();
        }
    }
    
    public static String createConnectionString(String hostName,
            String username, String password, String remoteFilePath) {
        // result: "sftp://user:123456@domainname.com/resume.pdf
        return "sftp://" + username + ":" + password + "@" + hostName
                + remoteFilePath;
    }

    public static FileSystemOptions createDefaultOptions() throws FileSystemException {
        // Create SFTP options
        FileSystemOptions opts = new FileSystemOptions();
        
        // SSH Key checking
        SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(
                opts, "no");
        
        // Root directory set to user home
        SftpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(opts, false);
        
        // Timeout is count by Milliseconds
        SftpFileSystemConfigBuilder.getInstance().setTimeout(opts, 10000);
        
        return opts;
    }

}


Then a quick unit test that I comment out Just because I am not interested really in regression test but rather just testing if the method works as expected (I do not like main() methods inside the classes)
package com.nestorurquiza.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import org.junit.Test;
import org.xml.sax.SAXException;


public class SftpUtilsTest {
    //@Ignore
    @Test    
    public void uploadTest() throws IOException, Exception, SAXException {
        String hostName = "bhubint.nestorurquiza.com";
        String userame = "user";
        String password = "pass";
        String localFilePath = "/tmp/test.txt";
        String remoteFilePath = "/home/report/report/test.txt";
        //SftpUtils.upload(hostName, userame, password, localFilePath, remoteFilePath);
        File f = new File(localFilePath);
        if (!f.exists())
            throw new RuntimeException("Error. Local file not found");
        FileInputStream fileInputStream = new FileInputStream(f);
        SftpUtils.upload(hostName, userame, password, fileInputStream, "test.txt", remoteFilePath);
    }
}

1 comment:

bigbird said...

edtFTPj/PRO is a commercial SFTP library supporting connection pools as well as FTP and FTPS. I'm one of the authors.

Followers