使用Apache Commons VFS SFTP跨服务器上传遇FileSystemException求助
解决Apache Commons VFS SFTP上传时的"relative path, no base URI"异常
我看到你在用Apache Commons VFS实现SFTP文件上传时遇到了org.apache.commons.vfs2.FileSystemException,提示路径是相对路径且未提供base URI。这个问题主要由两个核心原因导致,我来一步步帮你解决:
问题根源分析
密码中的特殊字符破坏了URI结构
你的密码kkTTpp@JacobWPST包含@符号,而SFTP URI中@是用来分隔「用户名:密码」和「主机地址」的关键标识。直接拼接的话,生成的URI会变成:sftp://root:kkTTpp@JacobWPST@111.222.333.444/root/home/tempfileholder/sample.txtVFS会错误地将
JacobWPST@111.222.333.444识别为主机地址,后面的/root/...部分就被当成了相对路径,从而抛出异常。代码中的字符串语法错误
你的main方法里localFilePath的定义有语法问题:String localFilePath = ""C:"+File.separator+"Users"+...;开头多了一个双引号,应该改为
"C:",否则代码根本无法编译。
解决方案
1. 对密码中的特殊字符进行URL编码
需要将密码中的特殊字符(比如@、:、/等)转换成URL编码格式,@对应的编码是%40。你可以手动替换,或者用URLEncoder自动编码。
2. 修正URI拼接逻辑
确保生成的SFTP URI格式正确,同时修复字符串语法错误。
修正后的完整代码
import java.io.File; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; 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; /** * The class SFTPUtil containing uploading, downloading, checking if file exists * and deleting functionality using Apache Commons VFS (Virtual File System) * Library * * @author Kavindu * */ public class SFTPUtility { public static void main(String[] args) { String hostName = "111.222.333.444"; String username = "root"; String password = "kkTTpp@JacobWPST"; // 修正localFilePath的语法错误 String localFilePath = "C:" + File.separator + "Users" + File.separator + "kavindu" + File.separator + "Desktop" + File.separator + "temp" + File.separator + "tempdetails.txt"; String remoteFilePath = "/root/home/tempfileholder/sample.txt"; upload(hostName, username, password, localFilePath, remoteFilePath); } /** * Method to upload a file in Remote server * * @param hostName * HostName of the server * @param username * UserName to login * @param password * Password to login * @param localFilePath * LocalFilePath. Should contain the entire local file path - * Directory and Filename with \\ as separator */ public static void upload(String hostName, String username, String password, String localFilePath, String remoteFilePath) { File file = new File(localFilePath); if (!file.exists()) throw new RuntimeException("Error. Local file not found"); StandardFileSystemManager manager = new StandardFileSystemManager(); try { manager.init(); // Create local file object FileObject localFile = manager.resolveFile(file.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(); } } /** * Generates SFTP URL connection String * * @param hostName * HostName of the server * @param username * UserName to login * @param password * Password to login * @param remoteFilePath * remoteFilePath. Should contain the entire remote file path - * Directory and Filename with / as separator * @return concatenated SFTP URL string */ public static String createConnectionString(String hostName, String username, String password, String remoteFilePath) { try { // 对密码进行URL编码,处理特殊字符 String encodedPassword = URLEncoder.encode(password, StandardCharsets.UTF_8.toString()); return "sftp://" + username + ":" + encodedPassword + "@" + hostName + remoteFilePath; } catch (Exception e) { // 编码失败时返回原密码拼接(作为 fallback) return "sftp://" + username + ":" + password + "@" + hostName + remoteFilePath; } } /** * Method to setup default SFTP config * * @return the FileSystemOptions object containing the specified * configuration options * @throws FileSystemException */ 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 (false means use system root) SftpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(opts, false); // Timeout is count by Milliseconds SftpFileSystemConfigBuilder.getInstance().setTimeout(opts, 10000); return opts; } }
额外注意事项
- 如果你不想手动处理URL编码,也可以用另一种方式创建远程文件对象,避免URI拼接的问题:
// 替代resolveFile的方式 String sftpUri = "sftp://" + hostName; FileSystemOptions opts = createDefaultOptions(); // 单独设置用户名密码 SftpFileSystemConfigBuilder.getInstance().setUsername(opts, username); SftpFileSystemConfigBuilder.getInstance().setPassword(opts, password); FileObject sftpRoot = manager.resolveFile(sftpUri, opts); FileObject remoteFile = sftpRoot.resolveFile(remoteFilePath); - 生产环境中不建议关闭严格主机密钥检查(
setStrictHostKeyChecking(opts, "no")),建议添加已知主机密钥来提升安全性。
内容的提问来源于stack exchange,提问作者Kavindu Akash
相关产品推荐
相关产品推荐

