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

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

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

1、版本

		<dependency>
			<groupId>commons-net</groupId>
			<artifactId>commons-net</artifactId>
			<version>3.6</version>
		</dependency>

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);

	}

}

  • 7
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Java FtpsClient 是 Apache Commons Net 库中的一个类,用于实现 FTP 客户端的功能。隐式 FTPS 是一种安全传输协议,与普通的 FTP 相比,隐式 FTPS 通过在传输过程中使用 SSL/TLS 加密技术来实现更高级别的数据传输安全。 要设置隐式 FTPS,首先需要创建一个 FtpsClient 对象,并通过 setEnabledProtocols() 方法来设置使用的传输协议。例如,可以使用如下代码来设置使用隐式 FTPS: FtpsClient ftpsClient = new FtpsClient(); ftpsClient.setEnabledProtocols(new String[]{"TLSv1.2"}); 上述代码将设置 FtpsClient 使用 TLSv1.2 协议来进行传输。 接下来,需要通过 connect() 方法连接到 FTP 服务器,并通过 feteServerSocketFactory() 方法创建一个 SSLSocketFactory 对象,用于为数据传输创建加密的套接字。例如,可以使用如下代码来进行连接和创建 SSLSocketFactory 对象: ftpsClient.connect("ftp.example.com", 990); SSLSocketFactory sslSocketFactory = (SSLSocketFactory) SSLSecureRandomFactory.getDefault().createSocketFactory(); 然后,可以使用 setSocketFactory() 方法将 SSLSocketFactory 对象设置给 FtpsClient,以确保在传输时使用 SSL/TLS 加密。例如,可以使用如下代码将 SSLSocketFactory 对象设置给 FtpsClientftpsClient.setSocketFactory(sslSocketFactory); 最后,通过登录 FTP 服务器的用户名和密码进行身份验证,并执行其他 FTP 操作,例如上传、下载文件等。 需要注意的是,要成功连接和设置隐式 FTPS,服务器端必须正确配置并支持隐式 FTPS 协议。同时,以上代码仅为示例,实际中可能需要进行更多的异常处理和其他设置。 总结起来,设置 Java FtpsClient 使用隐式 FTPS 需要创建 FtpsClient 对象、设置使用的传输协议、连接 FTP 服务器、创建和设置 SSLSocketFactory 对象,以及进行身份验证和执行 FTP 操作。以上所述为一种常用的实现方式,具体可以根据需要进行适当调整。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值