Fork me on GitHub

Programming Design Notes

使用 Apache Commons VFS 來操作 SFTP

| Comments

Java 使用 FTP 去上載檔案,下載檔案或去做其他操作也是很簡單,因為 Java API 已經包括了對 FTP 的支援。如果不是使用 FTP,而是使用 SFTP: SSH File Transfer Protocol,使用Java API 實作便有點麻煩了。

幸好 Apache Commons VFS 替我們實作了 SFTP 的操作,Apache Commons VFS 配合 JSch 使我們可以很簡單地使用 SSH 連接 Server 和上載檔案,下載檔案等等操作。

廢話不多說,首先要準備 3 個 Library:

下載好後將 .jar 加到 class path 令程式找到這些 Library

再來就是寫程式,為了方便好看,我會將所有程式碼放到 Main class 內。

第一步是製作一個 Function 去將 ProtocolHost NameUsernamePasswordServer 上的檔案位置連接起來 (Factory Method)。令到 Apache Commons VFS 能讀得懂。
public static String createConnectionString(String hostName,
String username, String password, String remoteFilePath) {
// result: "sftp://user:[email protected]/resume.pdf
return "sftp://" + username + ":" + password + "@" + hostName + "/"
+ remoteFilePath;
}

第二步是製作一個 Function 去製造出一個 SFTP 預設的設定 (Factory Method):
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, true);

// Timeout is count by Milliseconds
SftpFileSystemConfigBuilder.getInstance().setTimeout(opts, 10000);

return opts;
}

第三步是製作一個上載檔案的 Function:
public static void upload(String hostName, String username,
String password, String localFilePath, String remoteFilePath) {

File f = new File(localFilePath);
if (!f.exists())
throw new RuntimeException("Error. Local file not found");

StandardFileSystemManager manager = new StandardFileSystemManager();

try {
manager.init();

// Create local file object
FileObject localFile = manager.resolveFile(f.getAbsolutePath());

// Create remote file object
FileObject remoteFile = manager.resolveFile(
createConnectionString(hostName, username, password,
remoteFilePath), createDefaultOptions());

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

System.out.println("File upload success");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
manager.close();
}
}

在這裡可以測試一下上載檔案的 Function 是否沒有問題:
/*
* args[0]: hostName
* args[1]: username
* args[2]: password
* args[3]:localFilePath
* args[4]: remoteFilePath
*/
public static void main(String[] args) {
if (args.length < 5)
throw new RuntimeException(
"Error. Please enter "
+ "args[0]: hostName, args[1]: username, args[2]: password, "
+ "args[3]: localFilePath, args[4]: remoteFilePath.");

upload(args[0], args[1], args[2], args[3], args[4]);
}

成功後 Console 會打印出 File upload success

再來是加下載檔案的 Function:
public static void download(String hostName, String username,
String password, String localFilePath, String remoteFilePath) {

StandardFileSystemManager manager = new StandardFileSystemManager();

try {
manager.init();

String downloadFilePath = localFilePath.substring(0,
localFilePath.lastIndexOf("."))
+ "_downlaod_from_sftp"
+ localFilePath.substring(localFilePath.lastIndexOf("."),
localFilePath.length());

// Create local file object
FileObject localFile = manager.resolveFile(downloadFilePath);

// Create remote file object
FileObject remoteFile = manager.resolveFile(
createConnectionString(hostName, username, password,
remoteFilePath), createDefaultOptions());

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

System.out.println("File download success");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
manager.close();
}
}

測試檔案是否存在的 Function:
public static boolean exist(String hostName, String username,
String password, String remoteFilePath) {
StandardFileSystemManager manager = new StandardFileSystemManager();

try {
manager.init();

// Create remote object
FileObject remoteFile = manager.resolveFile(
createConnectionString(hostName, username, password,
remoteFilePath), createDefaultOptions());

System.out.println("File exist: " + remoteFile.exists());

return remoteFile.exists();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
manager.close();
}
}

刪除 Server 檔案的 Function:
public static void delete(String hostName, String username,
String password, String remoteFilePath) {
StandardFileSystemManager manager = new StandardFileSystemManager();

try {
manager.init();

// Create remote object
FileObject remoteFile = manager.resolveFile(
createConnectionString(hostName, username, password,
remoteFilePath), createDefaultOptions());

if (remoteFile.exists()) {
remoteFile.delete();
System.out.println("Delete remote file success");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
manager.close();
}
}

完整的程式:
package sftp.sample;

import java.io.File;

import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileSystemOptions;
import org.apache.commons.vfs.Selectors;
import org.apache.commons.vfs.impl.StandardFileSystemManager;
import org.apache.commons.vfs.provider.sftp.SftpFileSystemConfigBuilder;

public class Main {

/*
* args[0]: hostName
* args[1]: username
* args[2]: password
* args[3]: localFilePath
* args[4]: remoteFilePath
*/
public static void main(String[] args) {
if (args.length < 5)
throw new RuntimeException(
"Error. Please enter "
+ "args[0]: hostName, args[1]: username, args[2]: password, "
+ "args[3]: localFilePath, args[4]: remoteFilePath.");

upload(args[0], args[1], args[2], args[3], args[4]);
exist(args[0], args[1], args[2], args[4]);
download(args[0], args[1], args[2], args[3], args[4]);
delete(args[0], args[1], args[2], args[4]);
}

public static void upload(String hostName, String username,
String password, String localFilePath, String remoteFilePath) {

File f = new File(localFilePath);
if (!f.exists())
throw new RuntimeException("Error. Local file not found");

StandardFileSystemManager manager = new StandardFileSystemManager();

try {
manager.init();

// Create local file object
FileObject localFile = manager.resolveFile(f.getAbsolutePath());

// Create remote file object
FileObject remoteFile = manager.resolveFile(
createConnectionString(hostName, username, password,
remoteFilePath), createDefaultOptions());

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

System.out.println("File upload success");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
manager.close();
}
}

public static void download(String hostName, String username,
String password, String localFilePath, String remoteFilePath) {

StandardFileSystemManager manager = new StandardFileSystemManager();

try {
manager.init();

String downloadFilePath = localFilePath.substring(0,
localFilePath.lastIndexOf("."))
+ "_downlaod_from_sftp"
+ localFilePath.substring(localFilePath.lastIndexOf("."),
localFilePath.length());

// Create local file object
FileObject localFile = manager.resolveFile(downloadFilePath);

// Create remote file object
FileObject remoteFile = manager.resolveFile(
createConnectionString(hostName, username, password,
remoteFilePath), createDefaultOptions());

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

System.out.println("File download success");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
manager.close();
}
}

public static void delete(String hostName, String username,
String password, String remoteFilePath) {
StandardFileSystemManager manager = new StandardFileSystemManager();

try {
manager.init();

// Create remote object
FileObject remoteFile = manager.resolveFile(
createConnectionString(hostName, username, password,
remoteFilePath), createDefaultOptions());

if (remoteFile.exists()) {
remoteFile.delete();
System.out.println("Delete remote file success");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
manager.close();
}
}

public static boolean exist(String hostName, String username,
String password, String remoteFilePath) {
StandardFileSystemManager manager = new StandardFileSystemManager();

try {
manager.init();

// Create remote object
FileObject remoteFile = manager.resolveFile(
createConnectionString(hostName, username, password,
remoteFilePath), createDefaultOptions());

System.out.println("File exist: " + remoteFile.exists());

return remoteFile.exists();
} 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:[email protected]/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, true);

// Timeout is count by Milliseconds
SftpFileSystemConfigBuilder.getInstance().setTimeout(opts, 10000);

return opts;
}

}

相關書籍: Jakarta Commons CookbookPro Jakarta CommonsSSH, The Secure Shell: The Definitive Guide