Apache VFS: FTP
使用VFS 连接 FTP
近期项目 需要同时支持 本地/FTP/SFTP/HDFS 文件系统,需要在目标文件系统 读/写 文件,
VFS 虚拟文件系统 屏蔽了不同文件系统底层差异,提供统一接口实现,
因此选用 VFS 实现
VFS 连接FTP
VFS 的maven 坐标
<!-- vfs -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-vfs2</artifactId>
<version>2.6.0</version>
</dependency>
<!-- FTP -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
连接FTP demo
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.vfs2.*;
import org.apache.commons.vfs2.provider.ftp.FtpFileSystemConfigBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
/***
* demo
* @author yushangzuiyun2013
*/
public class VfsFtpDemo {
private static final FileSystemManager instance;
private static final Logger log = LoggerFactory.getLogger(VfsFtpDemo.class);
private final String host;
private final Integer port;
private final String userName;
private final String password;
public VfsFtpDemo(String host, Integer port, String userName, String password) {
this.host = host;
this.port = port == null ? 21 : port;
//需要转义,否则可能账号、密码出现 / %等字符,导致连接失败
this.userName = encodeSpecialChar(userName);
this.password = encodeSpecialChar(password);
}
public static void main(String[] args) {
VfsFtpDemo demo = new VfsFtpDemo("192.168.33.32", 21, "ftp-user", "123456");
try {
FileObject csvDir = demo.getFile("/source03/csv");
System.out.println("csvDir: " + csvDir.exists()+", "+csvDir.isFile());
} catch (FileSystemException e) {
log.error("获取文件失败!", e);
}
}
/**
* 获取文件
* @param path 文件路径,以/开头
* @return 文件对象
*/
public FileObject getFile(String path) throws FileSystemException {
path = path.startsWith("/") ? path : "/" + path;
String uri = getConnectUrl() + path;
return instance.resolveFile(uri, getOptions());
}
protected String getConnectUrl() {
StringBuilder sb = new StringBuilder();
sb.append("ftp://");
if (StringUtils.isNotEmpty(userName)) {
sb.append(userName).append(":").append(password).append("@");
}
sb.append(host);
sb.append(":").append(port);
// sb.append("/");
// sb.append(connInfo.getPath());
return sb.toString();
}
protected FileSystemOptions getOptions() {
FtpFileSystemConfigBuilder builder = FtpFileSystemConfigBuilder.getInstance();
FileSystemOptions options = new FileSystemOptions();
//解决中文乱码
builder.setServerLanguageCode(options, "zh");
// builder.setControlEncoding(options, "UTF-8");
builder.setAutodetectUtf8(options, true);
//设置超时时间
builder.setConnectTimeout(options, 30*1000);
//设置 被动模式,防止 由于防火墙导致连接不上
builder.setPassiveMode(options, true);
return options;
}
/**
* 对 连接信息中特殊字符进行编码
*/
protected String encodeSpecialChar(String str){
if(StringUtils.isEmpty(str)){
return str;
}
try {
return URLEncoder.encode(str, "UTF-8");
}catch (UnsupportedEncodingException e){
return str;
}
}
static {
try {
instance = VFS.getManager();
} catch (FileSystemException e) {
log.error("获取 VFS管理器 失败!", e);
throw new RuntimeException(e);
}
}
}
常见问题
windows 下能连,Linux上不行,甚至卡死
很可能是 未设置被动模式,默认为主动模式,会分配端口主动连接FTP,但服务器防火墙不允许,
导致一直连接不上,锁一直不释放
密码正确,但连不上FTP
有可能是 密码或用户名带有特殊字符,比如有@等,VFS解析时,错误截断,需要先转义
目录乱码
这要对 VFS设置FTP的文件名编码,代码中 getOptions 即是,可以参考