使用FTPSClient连接FTP下载文件,连接和登录都没有问题,但是下载文件时方法ftpsClient.listNames却报错:522 SSL connection failed; session reuse required: see require_ssl_reuse option in vsftpd.conf man page。原来是FTP的require_ssl_reuse=YES导致的,当选项require_ssl_reuse设置为
YES
时,所有SSL数据连接都需要显示SSL会话重用;证明他们知道与控制信道相同的主秘钥。联系客户把这个参数设置成NO之后果然可以下载了。如果设置为YES就没办法了吗,当然有办法,那就是使用SSL通道重用。
但FTPSClient目前是不支持ssl通道重用的,So,不要在浪费时间了。
不过可以重写FTPSClient达到目的。重写代码如下:
import org.apache.commons.net.ftp.FTPSClient;
import org.apache.commons.net.io.CopyStreamEvent;
import org.apache.commons.net.io.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.Locale;
public class SSLSessionReuseFTPSClient extends FTPSClient {
private static final Logger logger = LoggerFactory.getLogger(SSLSessionReuseFTPSClient.class);
/**
* @param command the command to get
* @param remote the remote file name
* @param local the local file name
* @return true if successful
* @throws IOException on error
* @since 3.1
*/
@Override
protected boolean _retrieveFile(String command, String remote, OutputStream local)
throws IOException {
Socket socket = _openDataConnection_(command, remote);
if (socket == null) {
return false;
}
final InputStream input;
input = new BufferedInputStream(socket.getInputStream());
// Treat everything else as binary for now
try {
Util.copyStream(input, local, getBufferSize(),
CopyStreamEvent.UNKNOWN_STREAM_SIZE, null,
false);
} finally {
Util.closeQuietly(input);
Util.closeQuietly(socket);
}
// Get the transfer response
boolean ok = completePendingCommand();
return ok;
}
@Override
protected void _prepareDataSocket_(final Socket socket) throws IOException {
if (socket instanceof SSLSocket) {
// Control socket is SSL
final SSLSession session = ((SSLSocket) _socket_).getSession();
final SSLSessionContext context = session.getSessionContext();
//context.setSessionCacheSize(preferences.getInteger("ftp.ssl.session.cache.size"));
try {
final Field sessionHostPortCache = context.getClass().getDeclaredField("sessionHostPortCache");
sessionHostPortCache.setAccessible(true);
final Object cache = sessionHostPortCache.get(context);
final Method method = cache.getClass().getDeclaredMethod("put", Object.class, Object.class);
method.setAccessible(true);
final String key = String.format("%s:%s", socket.getInetAddress().getHostName(),
String.valueOf(socket.getPort())).toLowerCase(Locale.ROOT);
method.invoke(cache, key, session);
} catch (NoSuchFieldException e) {
// Not running in expected JRE
logger.warn("No field sessionHostPortCache in SSLSessionContext", e);
} catch (Exception e) {
// Not running in expected JRE
logger.warn(e.getMessage());
}
}
}
}
import org.apache.commons.net.io.CopyStreamEvent;
import org.apache.commons.net.io.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.Locale;
public class SSLSessionReuseFTPSClient extends FTPSClient {
private static final Logger logger = LoggerFactory.getLogger(SSLSessionReuseFTPSClient.class);
/**
* @param command the command to get
* @param remote the remote file name
* @param local the local file name
* @return true if successful
* @throws IOException on error
* @since 3.1
*/
@Override
protected boolean _retrieveFile(String command, String remote, OutputStream local)
throws IOException {
Socket socket = _openDataConnection_(command, remote);
if (socket == null) {
return false;
}
final InputStream input;
input = new BufferedInputStream(socket.getInputStream());
// Treat everything else as binary for now
try {
Util.copyStream(input, local, getBufferSize(),
CopyStreamEvent.UNKNOWN_STREAM_SIZE, null,
false);
} finally {
Util.closeQuietly(input);
Util.closeQuietly(socket);
}
// Get the transfer response
boolean ok = completePendingCommand();
return ok;
}
@Override
protected void _prepareDataSocket_(final Socket socket) throws IOException {
if (socket instanceof SSLSocket) {
// Control socket is SSL
final SSLSession session = ((SSLSocket) _socket_).getSession();
final SSLSessionContext context = session.getSessionContext();
//context.setSessionCacheSize(preferences.getInteger("ftp.ssl.session.cache.size"));
try {
final Field sessionHostPortCache = context.getClass().getDeclaredField("sessionHostPortCache");
sessionHostPortCache.setAccessible(true);
final Object cache = sessionHostPortCache.get(context);
final Method method = cache.getClass().getDeclaredMethod("put", Object.class, Object.class);
method.setAccessible(true);
final String key = String.format("%s:%s", socket.getInetAddress().getHostName(),
String.valueOf(socket.getPort())).toLowerCase(Locale.ROOT);
method.invoke(cache, key, session);
} catch (NoSuchFieldException e) {
// Not running in expected JRE
logger.warn("No field sessionHostPortCache in SSLSessionContext", e);
} catch (Exception e) {
// Not running in expected JRE
logger.warn(e.getMessage());
}
}
}
}
调用方式:
private void downloadFile() {
logger.info("进入ftp下载方法");
SSLSessionReuseFTPSClient ftpsClient = new SSLSessionReuseFTPSClient();//("SSL", false);
FTPClientConfig config=new FTPClientConfig();
ftpsClient.configure(config);
ftpsClient.setAuthValue("TLS"); //如果FTP是AUTH TLS方式的
ftpsClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
ftpsClient.setDefaultTimeout(50000);
logger.info("开始连接ftp");
try {
ftpsClient.connect("127.0.0.1", "2211");
System.out.println("Connected to ftp");
System.out.print(ftpsClient.getReplyString());
int reply = ftpsClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftpsClient.disconnect();
logger.info("进入ftp下载方法");
SSLSessionReuseFTPSClient ftpsClient = new SSLSessionReuseFTPSClient();//("SSL", false);
FTPClientConfig config=new FTPClientConfig();
ftpsClient.configure(config);
ftpsClient.setAuthValue("TLS"); //如果FTP是AUTH TLS方式的
ftpsClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
ftpsClient.setDefaultTimeout(50000);
logger.info("开始连接ftp");
try {
ftpsClient.connect("127.0.0.1", "2211");
System.out.println("Connected to ftp");
System.out.print(ftpsClient.getReplyString());
int reply = ftpsClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftpsClient.disconnect();
System.err.println("FTP server refused connection.");
System.exit(1);
}
ftpsClient.login(jobContext.getRtFtpUsername(), jobContext.getRtFtpPassword());
ftpsClient.execPBSZ(0);
ftpsClient.execPROT("P");
ftpsClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
ftpsClient.enterLocalPassiveMode();
ftpsClient.setBufferSize(1024);
ftpsClient.setControlEncoding("GBK");
ftpsClient.setFileType(3);
ftpsClient.setDataTimeout(120000);
ftpsClient.setReceiveBufferSize(100000);
}
ftpsClient.login(jobContext.getRtFtpUsername(), jobContext.getRtFtpPassword());
ftpsClient.execPBSZ(0);
ftpsClient.execPROT("P");
ftpsClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
ftpsClient.enterLocalPassiveMode();
ftpsClient.setBufferSize(1024);
ftpsClient.setControlEncoding("GBK");
ftpsClient.setFileType(3);
ftpsClient.setDataTimeout(120000);
ftpsClient.setReceiveBufferSize(100000);
System.out.println("已经登陆FTP");
ftpsClient.changeWorkingDirectory(jobContext.getRtFtpPath());
FTPFile[] files = ftpsClient.listDirectories(jobContext.getRtFtpPath());
downSuccessFileList = new ArrayList<String>();
downFailFileList = new ArrayList<String>();
downOtherFileList = new ArrayList<String>();
for (FTPFile ftpFile : files) {
if (ftpFile.getName().startsWith(customerStoreCode)) {
String[] filenames = ftpsClient.listNames(jobContext.getRtFtpPath()+ftpFile.getName());
for (String filename : filenames) {
if (!filename.endsWith(".xlsx")) {
continue;
}
String subfilename=filename.substring(filename.lastIndexOf("/")+1, filename.length());
File localFile = new File(jobContext.getDowDir() + File.separator + subfilename);
OutputStream is = new FileOutputStream(localFile);
ftpsClient.retrieveFile(filename, is);
is.close();
downSuccessFileList.add(subfilename);
}
}
}
ftpsClient.logout();
} catch (Exception e) {
logger.error("FTP下载 ->>> 下载FTP数据文件失败!!。", e);
} finally {
if (ftpsClient.isConnected()) {
try {
ftpsClient.disconnect();
} catch (IOException ioe) {
logger.error("FTP下载 ->>> 下载FTP数据文件失败!!。", ioe.toString());
}
}
ftpsClient.changeWorkingDirectory(jobContext.getRtFtpPath());
FTPFile[] files = ftpsClient.listDirectories(jobContext.getRtFtpPath());
downSuccessFileList = new ArrayList<String>();
downFailFileList = new ArrayList<String>();
downOtherFileList = new ArrayList<String>();
for (FTPFile ftpFile : files) {
if (ftpFile.getName().startsWith(customerStoreCode)) {
String[] filenames = ftpsClient.listNames(jobContext.getRtFtpPath()+ftpFile.getName());
for (String filename : filenames) {
if (!filename.endsWith(".xlsx")) {
continue;
}
String subfilename=filename.substring(filename.lastIndexOf("/")+1, filename.length());
File localFile = new File(jobContext.getDowDir() + File.separator + subfilename);
OutputStream is = new FileOutputStream(localFile);
ftpsClient.retrieveFile(filename, is);
is.close();
downSuccessFileList.add(subfilename);
}
}
}
ftpsClient.logout();
} catch (Exception e) {
logger.error("FTP下载 ->>> 下载FTP数据文件失败!!。", e);
} finally {
if (ftpsClient.isConnected()) {
try {
ftpsClient.disconnect();
} catch (IOException ioe) {
logger.error("FTP下载 ->>> 下载FTP数据文件失败!!。", ioe.toString());
}
}
}
}
}