java FTPSClient 上传下载带证书的ftps服务器

网上一大把,测试不能用,谷歌了一下,发现少配置了一个环境变量。

System.setProperty("jdk.tls.useExtendedMasterSecret", "false");//设置环境变量

1、版本

		
			commons-net
			commons-net
			3.6
		

2、重写FTPSClient

ftps的require_ssl_reuse配置为YES,所有SSL数据连接都需要显示SSL会话重用,需要重写FTPSClient。

package ftps;

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;

import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;

import org.apache.commons.net.ftp.FTPSClient;
import org.apache.commons.net.io.CopyStreamEvent;
import org.apache.commons.net.io.Util;

/*
使用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达到目的。重写代码如下:
 */
public class SSLSessionReuseFTPSClient extends FTPSClient {

	/** 
	 * @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) {
				e.printStackTrace();
				// Not running in expected JRE
				System.out.println("No field sessionHostPortCache in SSLSessionContext");
			} catch (Exception e) {
				// Not running in expected JRE
				e.printStackTrace();
			}
		}
	}
}

3、封装FTPSUtil工具类

            注意ftp服务器是用的主动模式,还是被动模式。
            ftpsClient.enterRemotePassiveMode();// 主动模式,PORT模式。
            ftpsClient.enterLocalPassiveMode();// 被动模式,PASV模式。

package ftps;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPReply;

public class FTPSUtil {

	private SSLSessionReuseFTPSClient ftpsClient = null;

	/**
	 * 登陆
	 * @param ip
	 * @param port
	 * @param username
	 * @param password
	 * @date 2021年11月12日 下午5:03:14
	 */
	public boolean login(String ip, int port, String username, String password) {
		try {
			System.setProperty("jdk.tls.useExtendedMasterSecret", "false");//设置环境变量
			ftpsClient = new SSLSessionReuseFTPSClient();
			FTPClientConfig config = new FTPClientConfig();
			ftpsClient.configure(config);
			ftpsClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));// 打印调用日志,调试时使用,上线后注释
			ftpsClient.setControlEncoding("utf-8");// 编码设置必须放在connect前不然不起作用
			ftpsClient.connect(ip);
			ftpsClient.login(username, password);
			System.out.println("connecting...ftps服务器:" + ip + ":" + port);
			int reply = ftpsClient.getReplyCode();
			if (!FTPReply.isPositiveCompletion(reply)) {
				ftpsClient.disconnect();
				return false;
			}
			// ftpsClient.enterRemotePassiveMode();// 主动模式,PORT模式。通知客户端打开一个数据端口,服务端将连接到这个端口进行数据传输。
			ftpsClient.enterLocalPassiveMode();// 被动模式,PASV模式。通知服务器打开一个数据端口,客户端将连接到这个端口进行数据传输。
			ftpsClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);// 流传输模式
			ftpsClient.setDataTimeout(18000);// 数据连接超时(以毫秒为单位)
			ftpsClient.execPROT("P");// 数据通道保护级别
			ftpsClient.setFileType(FTP.BINARY_FILE_TYPE);
			ftpsClient.setBufferSize(1024);
			System.out.println("连接成功...ftp服务器:" + ip + ":" + port);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * 上传文件
	 * @param directory 服务端路径
	 * @param file 本地文件路径
	 * @date 2021年11月12日 下午5:03:14
	 */
	public boolean uploadFile(String directory, File file) {
		try {
			createDirectory(directory);
			ftpsClient.storeFile(file.getName(), new FileInputStream(file));
			ftpsClient.logout();
			return true;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (ftpsClient.isConnected()) {
				try {
					ftpsClient.disconnect();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return false;
	}

	/**
	 * 创建多层目录文件,如果有ftp服务器已存在该文件,则不创建,如果无,则创建
	 * @param remote 目录
	 * @date 2021年11月12日 下午5:03:14
	 */
	public boolean createDirectory(String remote) {
		String directory = remote;
		try {
			if (!remote.endsWith("/")) {
				directory = directory + "/";
			}
			// 如果远程目录不存在,则递归创建远程服务器目录
			if (!directory.equals("/") && !ftpsClient.changeWorkingDirectory(directory)) {
				int start = 0;
				int end = 0;
				if (directory.startsWith("/")) {
					start = 1;
				} else {
					start = 0;
				}
				end = directory.indexOf("/", start);
				String paths = "", path = "";
				while (true) {
					String subDirectory = remote.substring(start, end);
					path = path + "/" + subDirectory;
					// 目录不存在就创建
					if (!ftpsClient.changeWorkingDirectory(subDirectory)) {
						if (ftpsClient.makeDirectory(subDirectory)) {
							ftpsClient.changeWorkingDirectory(subDirectory);
						}
					}
					paths = paths + "/" + subDirectory;
					start = end + 1;
					end = directory.indexOf("/", start);
					// 检查所有目录是否创建完毕
					if (end <= start) {
						break;
					}
				}
			}
			return true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * 下载文件
	 * @param directory 服务端路径
	 * @param file 本地文件路径
	 * @date 2021年11月12日 下午5:03:14
	 */
	public boolean downloadFile(String directory, File file) {
		FileOutputStream fos = null;
		try {
			// ftpFiles ftpFiles = ftpsClient.listFiles(ftpsClient.printWorkingDirectory());
			// FTPFile[] ftpFiles = ftpsClient.listFiles(directory);
			String[] listNames = ftpsClient.listNames(directory);
			for (String path : listNames) {
				if (path.endsWith(file.getName())) {
					if (!file.getParentFile().exists()) {
						file.getParentFile().mkdirs();
					}
					System.out.println("本地保存路径:" + file.getAbsolutePath());
					fos = new FileOutputStream(file);
					ftpsClient.retrieveFile(path, fos);
					System.out.println("下载文件成功");
					return true;
				}
			}
			ftpsClient.logout();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (ftpsClient.isConnected()) {
				try {
					ftpsClient.disconnect();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return false;
	}

}

4、测试

package ftps;

import java.io.File;

public class Test {

	public static void main(String[] args) {
		String host = "192.168.5.200";
		int port = 21;
		String userName = "bossftps";
		String passWord = "123456";

		FTPSUtil ftps = new FTPSUtil();
		ftps.login(host, port, userName, passWord);

		String directory = "/data/目录1/目录2";
		File file = new File("c:/test/你好.txt");
		ftps.uploadFile(directory, file);

		ftps.login(host, port, userName, passWord);
		directory = "/data/目录1/目录2/你好.txt";
		ftps.downloadFile(directory, file);

	}

}

你可能感兴趣的:(java,FTPSClient,java,ftps,Java,FTPSClient)