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();
        FileObject localFile;
        FileObject remoteFile;

        try {
            manager.init();

            FileSystemOptions fileSystemOptions = createDefaultOptions();
            
            // Create local file object
            //FileObject localFile = manager.resolveFile(f.getAbsolutePath());
            localFile = manager.resolveFile("ram://path/needed/" + localInputStreamName);
            localFile.createFile();
            OutputStream localOutputStream = localFile.getContent().getOutputStream();
            IOUtils.copy(localInputStream, localOutputStream);
            localOutputStream.flush();
            
            // Create remote file object
            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 {
            remoteFile != null ? remoteFile.close();
            localFile != null ? localFile.close();
            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);
    }
}

4 comments:

bigbird said...
This comment has been removed by a blog administrator.
Unknown said...

I think we must close and delete localFile after coppying

Unknown said...

Do we need to close and and delete localFile?

Nestor Urquiza said...

Nhật Quang Phan Thanks. You are correct. In fact the removeFile needs to be closed as well. Just added both lines.

Followers