最近使用commons-net的FTP时,在传输较大文件的过程中,会出现文件重传的现象,即文件传输完毕之后,文件的大小会重新从零往上增长,直至和原文件大小一致,然后重复几次之后,后台日志中会报read time out的错误。

         我这个是中间经过了一个防火墙,如果没有类似情况的,可能不会出现这种问题。

 1、read time out问题解决

        可能是没有设置ftp noop,noop可以让ftp客户端定时向ftp服务器发送一个保持存活的指令,这样ftp客户端就可以跟ftp服务器长期保持连接状态。这个需要在登录之后设置

client.login(username, password);
//先登录,登录之后,设置Noop
client.setAutoNoopTimeout(12)

 2、文件重传,重传几次之后,出现read time out

        这个是因为ftp客户端和ftp服务器之间有一个防火墙导致的,中间没有防火墙的一般不会出现这个问题。由于个别防火墙会关闭长时间不使用的连接,而ftp在上传文件的过程中,会创建两个通道,一个进行数据传输,一个进行客户端和服务器之间的通信,在传输开始时,ftp客户端会通过通信通道告诉服务器传输的文件名称和版本信息,然后通过数据传输通道传输文件,在文件传输完成之后,通信通道才会再次被使用,通信传输完成等信息,然后整个文件才算传输完成。

        如果文件过大的话,比如传输过程持续时间比较长,这个时候,通信通道只是在开始传输的时候被使用一次,之后的时间一直处于闲置状态,等待文件传输完成之后再次通信,但是由于传输时间太长,导致防火墙误以为这个通信通道是不被使用的通道,然后防火墙就会主动关闭这个连接,这个时候设置Noop是不起作用的,因为Noop只会在客户端闲置的时候起作用,即客户端与服务器连接了,但是既不传输文件,也不进行其他命令的操作,这个时候Noop会定时向服务器发送消息以保持客户端和服务器的正常连接。

        当文件传输完成之后,客户端通过通信通道,向服务接收文件接收完毕的指令时,才发现这个通道已经被关闭了,由于一直收不到服务器的消息,客户端无法判断服务器是否完全接收文件,所以会重新上传文件到服务器,这也是为什么会出现文件重传的原因,由于收不到服务器发送的完成请求,所以,就会出现read time out。

解决办法:

        知道原因之后,再解决问题就好办了,通信通道是由于长时间不使用才被关闭的,那么只需要能让客户端在较短的时间内使用通信通道,防火墙就不会关闭该连接了,我的解决办法是,把大文件分成多个小文件进行传输,那么每个小文件传输完成之后,客户端和服务器都会通过通信通道进行通信,这样通信通道就处于频繁被使用状态了,这里只提供一个思路,肯定有更好的解决办法。

//文件过大分开上传文件
	private void splitUploadFile(FTPClient client,String fileName, InputStream inputStream, long restartAt,
								 long streamOffset, FTPDataTransferListener listener) throws IOException ,IllegalStateException,FTPIllegalReplyException,
								 FTPException,FTPDataTransferException,FTPAbortedException{
		//每次读取128M
		int eachSize = 1024 * 1024 * 128;
		int eachNum = getEachNum(inputStream,eachSize);
		byte[] eachFile = null;
		byte[] readFile = new byte[eachSize];
		InputStream stream = null;
		for (int i = 0; i < eachNum; i++){
			if (i == eachNum -1){
				eachFile = new byte[inputStream.available()];
				inputStream.read(eachFile);
				stream = new ByteArrayInputStream(eachFile);
				client.upload(fileName,stream,restartAt+i*eachSize,streamOffset,listener);
			}else {
				inputStream.read(readFile);
				stream = new ByteArrayInputStream(readFile);
				client.upload(fileName,stream,restartAt+i*eachSize,streamOffset,listener);
			}
		}
		stream.close();
	}

//获取需要传输多少次
private int getEachNum(InputStream inputStream, int eachSize) {
		if (inputStream ==null){
			return 0;
		}
		try {
			return (inputStream.available() - 1)/eachSize + 1;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return 0;
	}

        如果有跟我情况类似的,希望能解决各位的问题

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐