这几天在做一个FTP上传文件的功能,在本地运行完全没有问题,上传文件成功。在公网(centos系统)上进行测试的时候就出了问题了,上传一直卡到storeFile上,一般是会卡住30-60秒,然后返回“false”,不报异常。所以我又开始怀疑代码的问题,在本地建了个CentOS的虚拟机,安装vsftp,结果是成功的。使用FTP上传工具进行处理,公网上传下载一点问题都没有,不会出现0字节的问题,至此,我就十分无耐了,同样的代码,在不同环境上支行的效果居然不一样。
没办法,我只能想常规的想法一样,开始怀疑环境问题,按照说明,对公网的vsftp重新整了一遍,结果还是一样的,我去,什么鬼。
在百度搜到了千千万万的处理方法,一个的试,都没有处理好。其中有一个文章说到FTP的模式,VSFTP有两种模式:pasv模式与port模式,什么意思,不懂,再搜索一下。
Port模式:
当客户端C向服务端S连接后,使用的是Port模式,那么客户端C会发送一条命令告诉服务端S(客户端C在本地打开了一个端口N在等着你进行数据连接),当服务端S收到这个Port命令后 就会向客户端打开的那个端口N进行连接,这种数据连接就生成了。
Pasv模式:
当客户端C向服务端S连接后,服务端S会发信息给客户端C,这个信息是(服务端S在本地打开了一个端口M,你现在去连接我吧),当客户端C收到这个信息后,就可以向服务端S的M端口进行连接,连接成功后,数据连接也建立了。
从上面的解释中,大家可以看到两种模式主要的不同是数据连接建立的不同,对于Port模式,是客户端C在本地打开一个端口等服务端S去连接建立数据连接;而Pasv模式就是服务端S打开一个端口等待客户端C去建立一个数据连接。
默认情况下,FTPCLIENT用的是port模式,可以在FTPCLIENT原码中看到
private void__initDefaults(){
this.__dataConnectionMode = 0;
....
}
public voidenterLocalActiveMode() {
this.__dataConnectionMode = 0;
this.__passiveHost = null;
this.__passivePort = -1;
}
public voidenterLocalPassiveMode() {
this.__dataConnectionMode = 2;
this.__passiveHost = null;
this.__passivePort = -1;
} 不知道是不是这个问题,试一下吧,反正模式是客户端发起的,代码上加入ftpClient.enterLocalPassiveMode();
直接运行,成功。
附上ftp上传与下载代码:
import org.apache.commons.io.IOUtils;
import org.apache.commons.net.ftp.FTPClient;
import java.io.*;
import java.util.logging.Logger;
/**
* 含义:ftp的上传下载处理对象
*/
public class FtpFileTransfer {
private FTPClient ftpClient = null;//ftp客户端对象
private static String hostname = null;//FTP主机名称
private static Integer port = 0;//FTP服务端口
private static String userName = null;//FTP服务器登录用户名
private static String passwd=null;//FTP服务器登录密码
private final String SPAPRATE_TOEKN = "/";
private Logger logger = Logger.getLogger(this.getClass().getName());
/**
* 从配置文件中获取配置值
*/
public FtpFileTransfer(String hostname,int port,String userName,String passwd){
this.hostname = hostname;
this.port = port;
this.userName = userName;
this.passwd = passwd;
}
/**
* 方法描述:上传文件
* @param srcPath 源文件路径
* @param ftpPath FTP端存放路径
* @param targetName FTP端存放名称
* @return 操作是否成功
*/
public boolean uploadFile(String srcPath,String ftpPath, String targetName){
boolean ret = false;
File file = new File(srcPath);
ret = uploadFile(file, ftpPath, targetName);
return ret;
}
/**
* 方法描述:上传文件
* @param file 待上传的文件
* @param ftpPath 目标文件路径
* @param targetName 目标名称
* @return
*/
public boolean uploadFile(File file,String ftpPath,String targetName){
if(file == null){
logger.info("File is null");
return false;
}
try {
InputStream is = new FileInputStream(file);
return uploadFileStream(is, ftpPath, targetName);
} catch (FileNotFoundException e) {
return false;
}
}
/**
* 方法描述:上传文件
* @param is 源文件流
* @param ftpPath 放置在服务器上的位置
* @param targetName 放置在服务器上的名称
* @return 操作是否成功
*/
public boolean uploadFileStream(InputStream is,String ftpPath, String targetName){
boolean ret = false;
if(is == null){
logger.info("File is null");
return ret;
}
ret = this.connect2Ftp();
if(!ret){
logger.info("connect to ftp server failure");
this.disconnect2Ftp();
return ret;
}
try {
boolean mkdir = this.makeDir(ftpPath);
if(ftpPath.startsWith(SPAPRATE_TOEKN)){
ftpPath = ftpPath.substring(ftpPath.indexOf(SPAPRATE_TOEKN)+SPAPRATE_TOEKN.length());
}
ret = ftpClient.changeWorkingDirectory(ftpPath);
if(ret){
ftpClient.setBufferSize(1024);
ftpClient.setControlEncoding("utf-8");
ftpClient.enterLocalPassiveMode();
ret = ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
if(ret){
ret = ftpClient.storeFile(targetName, is);
}
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("FTP客户端出错!", e);
} finally {
IOUtils.closeQuietly(is);
disconnect2Ftp();
}
return ret;
}
/**
* 方法描述:下载文件
* @param remoteFileName FTP上的文件名称,含路径
* @param localFilePath 取到文件到本地后文件的存放位置
* @return 操作是否成功
*/
public boolean downloadFile(String remoteFileName, String localFilePath){
boolean ret = false;
remoteFileName = remoteFileName.trim();
if(remoteFileName.startsWith(SPAPRATE_TOEKN)){
remoteFileName = remoteFileName.substring(remoteFileName.indexOf(SPAPRATE_TOEKN)
+SPAPRATE_TOEKN.length());
}else if(remoteFileName.startsWith("\\")){
remoteFileName = remoteFileName.substring(remoteFileName.indexOf("\\")
+"\\".length());
}
String path = null;
if(!remoteFileName.contains("//")){
if(remoteFileName.contains("/")){
remoteFileName=remoteFileName.replace("/", "\\");
}
}
path = remoteFileName.substring(0,remoteFileName.lastIndexOf("\\"));
String fileName = remoteFileName.substring(path.length()+2);
FileOutputStream fos = null;
try {
ret = connect2Ftp();
if(!ret){
disconnect2Ftp();
return ret;
}
ret = ftpClient.changeWorkingDirectory(path);
fos = new FileOutputStream(localFilePath);
ftpClient.setBufferSize(1024);
ftpClient.setControlEncoding("utf-8");
//设置文件类型(二进制)
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
ret = ftpClient.retrieveFile(fileName, fos);
} catch (IOException e) {
throw new RuntimeException("FTP客户端出错!", e);
} finally {
IOUtils.closeQuietly(fos);
disconnect2Ftp();
}
return ret;
}
/**
* 方法描述:删除FTP服务器上的文件
* @param filePath 文件名称
* @return 删除成功/失败
*/
public boolean deleteFile(String filePath){
boolean ret = false;
if(filePath == null || filePath.isEmpty() || filePath.equals("")){
return false;
}
try {
connect2Ftp();
ftpClient.changeWorkingDirectory("\\");
ret = ftpClient.deleteFile(filePath);
} catch (IOException e) {
}finally{
disconnect2Ftp();
}
return ret;
}
/**
* 方法描述:删除文件夹
* @param dirPath 文件夹路径
* @param force 是否强制删除
* @return 是否删除成功
*/
public boolean deleteDirs(String dirPath, boolean force){
boolean ret = false;
if(dirPath.startsWith(SPAPRATE_TOEKN)){
dirPath = dirPath.substring(dirPath.indexOf(SPAPRATE_TOEKN)+SPAPRATE_TOEKN.length());
}
String path = dirPath;
try {
ret = ftpClient.changeWorkingDirectory(dirPath);
String[] names = ftpClient.listNames();
if(force){
for(String name:names){
if(dirPath.endsWith(SPAPRATE_TOEKN)){
ret = deleteFile(dirPath+name);
}else{
ret = deleteFile(dirPath+SPAPRATE_TOEKN+name);
}
}
}
ret = ftpClient.changeWorkingDirectory("//");
while(true){
ret = ftpClient.removeDirectory(path);
if(path.contains(SPAPRATE_TOEKN)){
path = path.substring(0, path.lastIndexOf(SPAPRATE_TOEKN));
}else{
break;
}
}
} catch (Exception e) {
}
return ret;
}
/**
* 方法描述:连接到远端的FTP服务器
* @return 是否连接成功
*/
private boolean connect2Ftp(){
boolean ret = false;
ftpClient = new FTPClient();
try {
ftpClient.connect(hostname, port);
ret = ftpClient.login(userName, passwd);
logger.info("Finished login the ftp server, result="+ret);
} catch (Exception e) {
}
return ret;
}
/**
* 方法描述:断开ftp链接
*/
private void disconnect2Ftp(){
if(ftpClient != null){
try {
ftpClient.disconnect();
} catch (IOException e) {
}
}
}
/**
* 方法描述:根据目录要求创建ftp端的文件夹路径
* @param dir 文件夹路径,如:绝对路径,/ab/cde/ef/hg,相对路径, ab/cd
* @return 是否创建成功
* @throws IOException
*/
private boolean makeDir(String dir) throws IOException{
boolean ret = false;
int i = 0;
if(dir != null && !dir.isEmpty()){
String[] dirs = null;
int len = 0;
if(dir.contains("//")){
dir = dir.replace("//", SPAPRATE_TOEKN);
i = 1;
}
if(dir.contains(SPAPRATE_TOEKN)){
dirs = dir.split(SPAPRATE_TOEKN);
len = dirs.length;
StringBuffer sb = new StringBuffer();
for(;i
sb.append(dirs[i]);
sb.append(SPAPRATE_TOEKN);
ret = ftpClient.makeDirectory(sb.toString());
}
}else{
ret = ftpClient.makeDirectory(dir);
}
}
return ret;
}
public boolean isFileExist(String filePath) {
connect2Ftp();
boolean ret = false;
try {
String rt = ftpClient.getModificationTime(filePath);
if (rt != null && !rt.isEmpty()) {
ret = true;
}
} catch (IOException e) {
} finally {
disconnect2Ftp();
}
return ret;
}
}