当密码包含@时,“期望/遵循URI中的主机名”exception

我正在尝试将本地系统文件复制到服务器

package classes; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.Properties; import org.apache.commons.vfs.FileObject; 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 SendMyFiles { public static void main(String[] args) { SendMyFiles sendMyFiles = new SendMyFiles(); String fileToFTP = "zcol_30092013.xls"; sendMyFiles.startFTP(fileToFTP); } public boolean startFTP(String fileToFTP){ Properties prop = new Properties(); InputStream in = getClass().getResourceAsStream("/config.properties"); StandardFileSystemManager manager = new StandardFileSystemManager(); try { prop.load(in); String serverAddress = prop.getProperty("serverAddress").trim(); String userId = prop.getProperty("userId").trim(); String password = prop.getProperty("password").trim(); String remoteDirectory = prop.getProperty("remoteDirectory").trim(); String localDirectory = prop.getProperty("localDirectory").trim(); System.out.println("Cheking values "+serverAddress+" "+userId+" "+password+" "+remoteDirectory+" "+localDirectory); //check if the file exists String filepath = localDirectory; System.out.println("filepath "+filepath); File file = new File(filepath); System.out.println(file+" "+file.exists()); if (!file.exists()) throw new RuntimeException("Error. Local file not found"); //Initializes the file manager manager.init(); //Setup our SFTP configuration FileSystemOptions opts = new FileSystemOptions(); SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking( opts, "no"); SftpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(opts, true); SftpFileSystemConfigBuilder.getInstance().setTimeout(opts, 10000); //Create the SFTP URI using the host name, userid, password, remote path and file name String sftpUri= "sftp://" + userId + ":" + password + "@" + serverAddress + "/" + remoteDirectory+ fileToFTP; // Create local file object System.out.println("sftp uri "+sftpUri); System.out.println(file.getAbsolutePath()); FileObject localFile = manager.resolveFile(file.getAbsolutePath()); // Create remote file object FileObject remoteFile = manager.resolveFile(sftpUri, opts); // Copy local file to sftp server remoteFile.copyFrom(localFile, Selectors.SELECT_SELF); System.out.println("File upload successful"); } catch (Exception ex) { ex.printStackTrace(); return false; } finally { manager.close(); } return true; } } 

执行代码时执行以下exception:

 org.apache.commons.vfs.FileSystemException: Invalid absolute URI "sftp://vmsorbit:***@172.16.16.148/universe/files/zcol_30092013.xls". at org.apache.commons.vfs.provider.AbstractOriginatingFileProvider.findFile(AbstractOriginatingFileProvider.java:62) at org.apache.commons.vfs.impl.DefaultFileSystemManager.resolveFile(DefaultFileSystemManager.java:692) at org.apache.commons.vfs.impl.DefaultFileSystemManager.resolveFile(DefaultFileSystemManager.java:620) at classes.SendMyFiles.startFTP(SendMyFiles.java:67) at classes.SendMyFiles.main(SendMyFiles.java:23) Caused by: org.apache.commons.vfs.FileSystemException: Expecting / to follow the hostname in URI "sftp://vmsorbit:***@172.16.16.148/universe/files/zcol_30092013.xls". at org.apache.commons.vfs.provider.HostFileNameParser.extractToPath(HostFileNameParser.java:155) at org.apache.commons.vfs.provider.URLFileNameParser.parseUri(URLFileNameParser.java:49) at org.apache.commons.vfs.provider.AbstractFileProvider.parseUri(AbstractFileProvider.java:188) at org.apache.commons.vfs.provider.AbstractOriginatingFileProvider.findFile(AbstractOriginatingFileProvider.java:58) ... 4 more 

在线获得错误

 FileObject localFile = manager.resolveFile(file.getAbsolutePath()); 

密码包含特殊字符@

如果您的密码包含@ ,则URL解析器将其视为userinfo – hostname分隔符。 然后它会扫描一个主机名,在下一个@上停止,它会分隔实际的主机名。 接下来,它检查主机名后的第一个字符是/ ,它不是,因为它是@ 。 逻辑对我来说没有多大意义,但解释了令人困惑的错误信息

期望/遵循URI中的主机名

但无论如何,即使逻辑更好,你也不能在密码或用户名中使用文字@ 。 您必须将其URL编码为%40

如果您的用户名/密码是可变的,您应该使用UriParser.encode更好地对其进行编码:

 public static String encode(String decodedStr) 

请注意,文档中的注释是错误的。 它说方法“从字符串中删除%nn编码”。 ,虽然它实际上添加了它们。

正如@ martin-prikryl的回答所述,某些字符不能以原始forms出现在用户名和密码字段中,否则它们将使URI无效。

这里的根本原因是您使用简单的字符串连接来构造URI:

 String sftpUri= "sftp://" + userId + ":" + password + "@" + serverAddress + "/" + remoteDirectory+ fileToFTP; 

Java具有URI和URL类,可用于从各个字段构造URI和URL。 他们将正确处理每个字段的编码。 您应该使用其中一个而不是滚动自己的逻辑:

 import java.net.URI; import java.net.URISyntaxException; public class URITest { public static void main(String[] args) throws URISyntaxException { String user = "user"; String passwd = "p@ss/ord"; String host = "example.com"; String path = "/some/path"; String userInfo = user + ":" + passwd; URI uri = new URI("sftp", userInfo, host, -1, path, null, null); System.out.println(uri.toString()); } } 

这打印:

 sftp://user:p%40ss%2Ford@example.com/some/path