Java8的FTPS的Data resumed錯誤的補丁

程式語言版本:Java8

插件:commons-io-2.22.0.jarcommons-net-3.13.0.jarlog4j-1.2.17.jarslf4j-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;

}


}

留言