这几天在做一个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 void enterLocalActiveMode() { this.__dataConnectionMode = 0; this.__passiveHost = null; this.__passivePort = -1; } public void enterLocalPassiveMode() { 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<len;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; } }