Java8的FTPS的Data resumed錯誤的補丁
程式語言版本:Java8
插件:commons-io-2.22.0.jar、commons-net-3.13.0.jar、log4j-1.2.17.jar、slf4j-log4j13-1.0.1.jar
整個程式包:test_ftps_tls.zip
補丁的部分是從網路上搜尋到的,因為忘記來源是什麼了,所以我就保留補丁原本的class名稱,請有興趣的網友自行搜尋吧。
「log4j」是跟「slf4j-log4j13」配合的,請注意它的版本號是1,不是2,放2進去會不能用。
至於補丁要怎麼使用的話,我個人是使用Eclipse撰寫的,所以我把補丁跟主程式放在一起就可以用了,如果是使用其它撰寫工具的網友請自行測試一下了,這個程式我實測過確定是可以使用的,也可以在Centos6上面的Java8上面使用。
因為程式碼放入<code></code>裡面的話版面會跑掉,所以我就不放入Code標籤裡面了,這樣子看會比較清楚。
SharedSSLFTPSClient.java:
package test_ftps_tls;
import org.apache.commons.net.ftp.FTPSClient;
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.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.Locale;
public class SharedSSLFTPSClient extends FTPSClient{
public SharedSSLFTPSClient(String protocol, boolean isImplicit) {
super(protocol, isImplicit);
}
private static final Logger logger = LoggerFactory.getLogger(SharedSSLFTPSClient.class);
@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(0); // you might want to limit the cache
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);
String key = String.format("%s:%s", socket.getInetAddress().getHostName(),
String.valueOf(socket.getPort())).toLowerCase(Locale.ENGLISH);
method.invoke(cache, key, session);
key = String.format("%s:%s", socket.getInetAddress().getHostAddress(),
String.valueOf(socket.getPort())).toLowerCase(Locale.ENGLISH);
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());
}
}
}
}
Test_ftps_tls.java:
package test_ftps_tls;
/*
* 本程式支援Implicit FTPS(隱式 FTPS)以及Explicit FTPS(顯式 FTPS),
* Implicit FTPS(隱式 FTPS - - 990 port),即加密失敗時立即中斷連線。
* Explicit FTPS(顯式 FTPS - - 21 port),即先用普通明碼ftp連接,溝通加密機制之後再連接加密通道。
* Implicit FTPS 請在 SharedSSLFTPSClient(ssl_type, true)設定為true。
* Explicit FTPS 請在 SharedSSLFTPSClient(ssl_type, false)設定為false。
* */
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.HashMap;
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
public class Test_ftps_tls {
static SimpleDateFormat datetimeformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
static SimpleDateFormat datetimeformat1 = new SimpleDateFormat("yyyyMMddHHmmss");
static SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd");
static SimpleDateFormat dateformat1 = new SimpleDateFormat("yyyyMMdd");
static SimpleDateFormat timeformat = new SimpleDateFormat("HH:mm:ss");
static String ftpuser = "user";
static String ftppass = "123456789";
static String ftp_type = "ftps";
static int ftp_port = 21;
static String ftpdir = "/test/"; // 監視檔備份路徑
static String src_host = "192.168.0.1";
static String des_host = "192.168.0.2";
static String ftp_path = "/";
static String filerule = "*.mp4";
@SuppressWarnings("null")
public static void main(String[] args) throws IOException {
// TODO 自動產生的方法 Stub
HashMap<String, String> src_list = get_ftp_list(ftp_type,src_host,ftp_path,filerule);
//System.out.println("dir_list="+Arrays.asList(dir_list).toString());
// 帶FTP的輸入串流
System.out.println("準備傳輸檔案");
SharedSSLFTPSClient src_ftp = ftps_link("TLSv1.2", src_host, ftp_port, ftpuser, ftppass);
SharedSSLFTPSClient des_ftp = ftps_link("TLSv1.2", des_host, ftp_port, ftpuser, ftppass);
// 取得監視檔目錄清單
for(int i = 0;i<Integer.parseInt(src_list.get("total"));i++) {
System.out.println(src_list.get("path"+i)+src_list.get("name"+i));
InputStream in_File = src_ftp.retrieveFileStream(src_list.get("path"+i)+src_list.get("name"+i));
OutputStream out_File = des_ftp.appendFileStream(src_list.get("path"+i)+src_list.get("name"+i));
ftp2ftp(in_File,out_File);
//ftp.(ftppath+ftparray[i]);
}
}
public static FTPClient ftp_link(String host,int ftp_port,String ftpuser,String ftppass) throws SocketException, IOException{
FTPClient ftp = new FTPClient();
// 將 FTP 指令與伺服器回應輸出到主控台 (Console)
ftp.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true));
//ftps.connect(host,ftps_port);
ftp.connect(host,ftp_port);
ftp.login(ftpuser, ftppass);
// 必填:啟用被動模式,解決防火牆阻擋問題
System.out.println("使用被動模式連接ftp伺服器");
ftp.enterLocalPassiveMode();
ftp.setFileType(FTP.BINARY_FILE_TYPE); // 傳輸二進位檔案
System.out.println("ftp server port:"+ftp.getRemotePort());
ftp.setControlEncoding("UTF-8");
return ftp;
}
public static SharedSSLFTPSClient ftps_link(String ssl_type,String host,int ftp_port,String ftpuser,String ftppass) throws SocketException, IOException{
SharedSSLFTPSClient ftp = new SharedSSLFTPSClient(ssl_type, false);
// 將 FTP 指令與伺服器回應輸出到主控台 (Console)
ftp.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true));
//ftps.connect(host,ftps_port);
ftp.connect(host,ftp_port);
ftp.login(ftpuser, ftppass);
// 必填:啟用被動模式,解決防火牆阻擋問題
System.out.println("使用被動模式連接ftp伺服器");
ftp.enterLocalPassiveMode();
ftp.setFileType(FTP.BINARY_FILE_TYPE); // 傳輸二進位檔案
// 必填:針對資料通道進行加密防護 (PBSZ與PROT命令)
System.out.println("ftp server port:"+ftp.getRemotePort());
ftp.execPBSZ(0);
ftp.execPROT("P");
ftp.setControlEncoding("UTF-8");
return ftp;
}
public static void ftp2ftp(InputStream storeFile, OutputStream dvr2outFile) throws IOException {
// 帶SMB的輸出串流
@SuppressWarnings("resource")
//SmbFileOutputStream out = new SmbFileOutputStream(dvr2File);
BufferedOutputStream out = new BufferedOutputStream(dvr2outFile);
// 帶FTP的輸入串流
BufferedInputStream bf = new BufferedInputStream(storeFile);
// 創造緩衝區
byte[] bt = new byte[8192];
// 開始傳檔 (把串流的輸入與輸出接上)
int n = bf.read(bt);
while (n != -1) {
out.write(bt, 0, n);
out.flush();
n = bf.read(bt);
//run_check = true;
}
}
@SuppressWarnings("null")
public static HashMap<String, String> get_ftp_list(String ftp_type, String server_address, String path,String filerule) throws SocketException, IOException{
HashMap<String, String> file_array = new HashMap<String, String> ();
ftp_type = ftp_type.toLowerCase();
switch(ftp_type) {
case "ftp":
FTPClient ftp = new FTPClient(); // true 表示使用隱式 SSL/TLS (Implicit)
ftp.connect(src_host,ftp_port);
ftp.login(ftpuser, ftppass);
// 將 FTP 指令與伺服器回應輸出到主控台 (Console)
ftp.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true));
// 必填:啟用被動模式,解決防火牆阻擋問題
System.out.println("使用被動模式連接ftp伺服器");
ftp.enterLocalPassiveMode();
if(ftp.getPassivePort()<0) {
System.out.println("改用主動模式連接ftp伺服器");
ftp.enterLocalActiveMode();
}
ftp.setFileType(FTP.BINARY_FILE_TYPE); // 傳輸二進位檔案
System.out.println("ftp server port:"+ftp.getRemotePort());
ftp.setControlEncoding("UTF-8");
FTPFile[] ftp_files = ftp.listFiles(path);
int array_total = 0;
HashMap<String, String> tmp_array = parse_rawlist(ftp_files);
for(int i = 0;i<Integer.valueOf(tmp_array.get("total"));i++) {
if(tmp_array.get("name"+i).matches(filerule.replace("*", "(.*)"))) {
file_array.put("path"+array_total, path);
file_array.put("name"+array_total, tmp_array.get("name"+i));
array_total++;
}
}
file_array.put("total", String.valueOf(array_total));
//System.out.println("ftp_files:"+Arrays.asList(ftp_files).toString());
ftp.logout();
break;
case "ftps":
//FTPSClient ftps = new FTPSClient(true); // true 表示使用隱式 SSL/TLS (Implicit)
//SharedSSLFTPSClient ftps = new SharedSSLFTPSClient("TLSv1.2", true);
SharedSSLFTPSClient ftps = new SharedSSLFTPSClient("TLSv1.2", false);
// 將 FTP 指令與伺服器回應輸出到主控台 (Console)
ftps.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true));
//ftps.connect(host,ftps_port);
ftps.connect(src_host,ftp_port);
ftps.login(ftpuser, ftppass);
// 必填:啟用被動模式,解決防火牆阻擋問題
System.out.println("使用被動模式連接ftp伺服器");
ftps.enterLocalPassiveMode();
ftps.setFileType(FTP.BINARY_FILE_TYPE); // 傳輸二進位檔案
// 必填:針對資料通道進行加密防護 (PBSZ與PROT命令)
System.out.println("ftp server port:"+ftps.getRemotePort());
ftps.execPBSZ(0);
ftps.execPROT("P");
ftps.setControlEncoding("UTF-8");
FTPFile[] ftps_files = ftps.listFiles(path);
int array_total2 = 0;
HashMap<String, String> tmp_array2 = parse_rawlist(ftps_files);
for(int i = 0;i<Integer.valueOf(tmp_array2.get("total"));i++) {
if(tmp_array2.get("name"+i).matches(filerule.replace("*", "(.*)"))) {
System.out.println("path:"+path);
file_array.put("path"+array_total2, path);
file_array.put("name"+array_total2, tmp_array2.get("name"+i));
array_total2++;
}
}
file_array.put("total", String.valueOf(array_total2));
//System.out.println("ftps_files:"+Arrays.asList(ftps_files).toString());
ftps.logout();
break;
}
return file_array;
}
public static HashMap<String, String> parse_rawlist( FTPFile[] array ) {
HashMap<String, String> structure = new HashMap<String, String>();
Integer tmp_count = 0 ;
for ( int i = 0; i < array.length; i++ ) {
FTPFile current = array[i];
//System.out.println(datetimeformat.format(current.getTimestamp().getTime()));
if(current.getName().length() > 1) {
String[] current_datetime_array = datetimeformat.format(current.getTimestamp().getTime()).split(" ");
String[] current_date_array = current_datetime_array[0].split("-");
String[] current_time_array = current_datetime_array[1].split(":");
structure.put("is_dir" + tmp_count, String.valueOf(current.isDirectory()));
//$structure[$i]["perms"] = substr($current, 1, 9);
//$structure[$i]["number"] = trim(substr($current, 11, 1));
//$structure[$i]["owner"] = trim(substr($current, 13, 3));
//$structure[$i]["group"] = trim(substr($current, 17, 3));
structure.put("size" + tmp_count, Long.toString(current.getSize()));
structure.put("month" + tmp_count, current_date_array[1]);
structure.put("day" + tmp_count, current_date_array[2]);
structure.put("time" + tmp_count, current_time_array[0]+current_time_array[1]+current_time_array[2]);
structure.put("name" + tmp_count, current.getName());
tmp_count++;
}
}
structure.put("total", String.valueOf(tmp_count));
return structure;
}
}
留言