这段时间一直忙着项目开发,没顾上写总结。今天抽点时间对于之前遇到的ftp上传文件无应答的问题总结一下,方便自己以后查看,也方便下个遇到此问题的人能够快速的解决问题。
使用Apache的开源框架写的一个很简单的ftp上传的demo,如果网络环境稳定的情况下上传文件不会有问题,也能得到服务器文件传完后的响应状态,但是遇到大文件和网络不好的情况下,上传文件时间比较长(半个小时以上),这时执行ftpClient.storeFile("test.ts", in)这句代码会卡在这里,即使文件传完了也不会做出响应,得不到服务器的响应代码就不会继续往下执行,线程也就会一直停在这里。下面是一个简单的文件ftp上传demo:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
public class FtpUtil {
public static void main(String[] args) {
FTPClient ftpClient = new FTPClient();
try {
// 连接指定服务器,默认端口为21
ftpClient.connect("127.0.0.1");
System.out.println("connect to server");
// 获取响应字符串(FTP服务器上可设置)
String replyString = ftpClient.getReplyString();
System.out.println("replyString: " + replyString);
// 获取响应码用于验证是否连接成功
int reply = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
System.out.println(reply);
System.exit(1);
}
// 设置链接编码,windows主机UTF-8会乱码,需要使用GBK或gb2312编码
ftpClient.setControlEncoding("GBK");
// 登录服务器
boolean login = ftpClient.login("ceshi", "ceshi");
if (login) {
System.out.println("登录成功!");
} else {
System.out.println("登录失败!");
}
// 获取所有文件和文件夹的名字
FTPFile[] files = ftpClient.listFiles();
for (FTPFile file : files) {
if (file.isDirectory()) {
System.out.println(file.getName() + " 是文件夹");
}
if (file.isFile()) {
System.out.println(file.getName() + " 是文件");
}
}
// 生成InputStream用于上传本地文件
InputStream in = new FileInputStream("e:\\ceshi.txt");
// 上传文件
boolean result = ftpClient.storeFile("test.txt", in);
if (result) {
System.out.println("上传成功!");
}
in.close();
// 注销登录
boolean logout = ftpClient.logout();
if (logout) {
System.out.println("注销成功!");
} else {
System.out.println("注销失败!");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭链接需要放在finally语句块中
if (ftpClient.isConnected()) {
try {
ftpClient.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
解决方案:
遇到这样的问题刚开始不知道怎么回事,经过一番排查后找到了问题的原因,最后给出下面两种解决方法。
1、这个框架提供了多种上传方法,比如:
可以通过使用直接获取远程目录创建文件、然后拿到输出流将文件写入,这样可以手动控制文件的上传,也可以实现断点续传。这是一种办法,推荐使用这种办法。
2、这个方案是通过主线程和子线程的方式来实现,将文件上传放在子线程里,主线程负责开启上传子线程和监听子线程文件上传是否完成,如果遇到不响应的情况,主线程会判断文件上传是否完成,如果完成则强制关闭子线程使用的文件流,然后读取新的文件继续开启新的线程来执行上传。