Apache commons ftp API 的Spike

因爲公司給了我一個傳FTP的任務,恰好能夠學習一下,同時也進行了一些單元測試,spike了一把,哈哈。分享一下。 java

這裏只對最簡單經常使用的FtpClient的上傳下載列表功能進行測試學習使用^-^ apache

建立一個maven工程 服務器

在工做根目錄建立目錄 app

工程path/res/log maven

工程path/res/ftphome ide

配置pom文件。 單元測試

<dependencies>
		<dependency>
			<groupId>org.apache.ftpserver</groupId>
			<artifactId>ftpserver-core</artifactId>
			<version>1.0.6</version>
		</dependency>


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

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.5.2</version>
		</dependency>

		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.14</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
配置建立一個log4j.properties文件,放在src/test/resource下
log4j.rootLogger=INFO, stdout ,R 

log4j.appender.R=org.apache.log4j.RollingFileAppender 
log4j.appender.R.File=./res/log/ftpd.log
log4j.appender.R.MaxFileSize=10MB 
log4j.appender.R.MaxBackupIndex=10 
log4j.appender.R.layout=org.apache.log4j.PatternLayout 
log4j.appender.R.layout.ConversionPattern=[%5p] %d [%X{userName}] [%X{remoteIp}] %m%n

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss}-%m%n
在src/test/resource下

建立一個目錄upload/ 學習

存放兩個文件 upload/rober.c.martin.jpg, upload/excel.xslx 測試

仍是在src/test/resource下加一個myusers.properties文件 this

ftpserver.user.test.userpassword=test
ftpserver.user.test.homedirectory=./res/ftphome
ftpserver.user.test.enableflag=true
ftpserver.user.test.writepermission=true
ftpserver.user.test.maxloginnumber=3
ftpserver.user.test.maxloginperip=3
ftpserver.user.test.idletime=0
ftpserver.user.test.uploadrate=0
ftpserver.user.test.downloadrate=0
一切準備好了,首先咱們來測試FtpServer的啓動

src/test/java/com/fengzidm/spike/ftp/TestFtpServer.java

package com.fengzidm.spike.ftp;

import static org.junit.Assert.*;

import java.io.File;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.ftpserver.FtpServer;
import org.apache.ftpserver.FtpServerFactory;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.usermanager.ClearTextPasswordEncryptor;
import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public class TestFtpServer
{

	private static final String HOST = "127.0.0.1";
	private static final int PORT = 2222;
	
	 // 與myusers.properties配置相結合

        private static final String USERNAME = "test";
	private static final String PASSWORD = "test";

	private static FtpServer server;

	@BeforeClass
	public static void setUpBeforeClass() throws Exception
	{
		try
		{
			FtpServerFactory serverFactory = new FtpServerFactory();

			ListenerFactory listenerFactory = new ListenerFactory();
			listenerFactory.setPort( PORT );
			// replace the default listener
			serverFactory.addListener( "default", listenerFactory.createListener() );

			PropertiesUserManagerFactory userManagerFactory = new PropertiesUserManagerFactory();
			userManagerFactory.setFile( new File( "myusers.properties" ) );
			
			 //設置爲不加密碼形式的密碼
			userManagerFactory.setPasswordEncryptor( new ClearTextPasswordEncryptor() );
			serverFactory.setUserManager( userManagerFactory.createUserManager() );

			server = serverFactory.createServer();
			server.start();
		}
		catch ( Exception e )
		{
			e.printStackTrace();
			fail( e.getMessage() );
		}
	}

	@Test
	public void testConnect() throws Exception
	{

		FTPClient ftpClient = new FTPClient();
		ftpClient.connect( HOST, PORT );

		Integer replyCode = ftpClient.getReplyCode();
	
		//鏈接成功狀態碼爲220
		assertEquals( new Integer( 220 ), replyCode );
	}

	@Test
	public void testLoginSuccess() throws Exception
	{
		FTPClient ftpClient = new FTPClient();
		ftpClient.connect( HOST, PORT );
		boolean result = ftpClient.login( USERNAME, PASSWORD );
		Integer replyCode = ftpClient.getReplyCode();
		assertTrue( result );
		assertEquals( new Integer( 230 ), replyCode );
	}

	@Test
	public void testLoginFailue() throws Exception
	{
		FTPClient ftpClient = new FTPClient();
		ftpClient.connect( HOST, PORT );

		String invalidPassword = "fuck";
		boolean result = ftpClient.login( USERNAME, invalidPassword );
		Integer replyCode = ftpClient.getReplyCode();
		assertFalse( result );

		 //登陸失敗返回狀態碼530
		assertEquals( new Integer( 530 ), replyCode );
	}

	@AfterClass
	public static void tearDown() throws Exception
	{
		if ( server != null )
		{
			try
			{
				server.stop();
				System.out.println( "testing server is stop" );
			}
			catch ( Exception igonred )
			{
			}
		}
	}
}
在測試FtpClient前

根據上面

建立一個FtpTestHelper類

src/test/java/com/fengzidm/spike/ftp/FtpTestHelper.java


package com.fengzidm.spike.ftp;

import java.io.File;

import org.apache.ftpserver.FtpServer;
import org.apache.ftpserver.FtpServerFactory;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.usermanager.ClearTextPasswordEncryptor;
import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory;
import org.junit.Assert;

public class FtpTestHelper
{
	private FtpServer ftpServer;

	public void initialize( int port, String userManagerProperties )
	{
		try
		{
			FtpServerFactory serverFactory = new FtpServerFactory();

			ListenerFactory listenerFactory = new ListenerFactory();
			listenerFactory.setPort( port );
			// replace the default listener
			serverFactory.addListener( "default", listenerFactory.createListener() );

			PropertiesUserManagerFactory userManagerFactory = new PropertiesUserManagerFactory();
			userManagerFactory.setFile( new File( userManagerProperties ) );

			
			 // 設置爲不加密碼形式的密碼
			userManagerFactory.setPasswordEncryptor( new ClearTextPasswordEncryptor() );
			serverFactory.setUserManager( userManagerFactory.createUserManager() );

			ftpServer = serverFactory.createServer();
		}
		catch ( Exception e )
		{
			e.printStackTrace();
			Assert.fail( "建立FtpServer失敗,緣由:" + e.getMessage() );
		}
	}

	public void startServer() throws FtpException
	{
		if ( ftpServer != null && ftpServer.isStopped() )
		{
			ftpServer.start();
		}
	}

	public void stopServer()
	{
		if ( ftpServer != null && !ftpServer.isStopped() )
		{
			ftpServer.stop();
		}
	}
}




建立一個TestCase類,幫組消除setUp 等這些代碼

src/test/java/com/fengzidm/spike/ftp/FtpClientTestCase.java


package com.fengzidm.spike.ftp;

import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.net.SocketException;

import org.apache.commons.net.ftp.FTPClient;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;

public abstract class FtpClientTestCase
{
	
	protected static final String HOST = "127.0.0.1";
	protected static final int PORT = 2222;

	//與myusers.properties配置相結合
        protected static final String USERNAME = "test";
	protected static final String PASSWORD = "test";

	protected static FtpTestHelper helper;

	@BeforeClass
	public static void setUpBeforeClass() throws Exception
	{
		helper = new FtpTestHelper();
		helper.initialize( PORT, "myusers.properties" );
		helper.startServer();
	}

	protected FTPClient ftpClient;

	@Before
	public void setUp() throws Exception
	{
		ftpClient = new FTPClient();
		ftpClient.connect( HOST, PORT );
		boolean result = ftpClient.login( USERNAME, PASSWORD );
		assertTrue( result );
	}

	@After
	public void tearDown() throws Exception
	{
		if ( ftpClient.isConnected() )
		{
			ftpClient.disconnect();
		}
	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception
	{
		helper.stopServer();
	}
	
	protected FTPClient reConnectFtpClient() throws SocketException, IOException
	{
		if ( ftpClient.isConnected() )
		{
			ftpClient.disconnect();
		}

		ftpClient = new FTPClient();
		ftpClient.connect( HOST, PORT );
		ftpClient.login( USERNAME, PASSWORD );
		return ftpClient;
	}
}
咱們進行FtpClient的API簡單使用的測試


src/test/java/com/fengzidm/spike/ftp/TestFtpClient.java


package com.fengzidm.spike.ftp;

import static org.junit.Assert.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPListParseEngine;
import org.junit.Test;

public class TestFtpClient extends FtpClientTestCase {

	/**
	 * 上傳文本文件
	 */
	@Test
	public void testStoreTextFile() throws Exception {
		ByteArrayInputStream bais = new ByteArrayInputStream(new String("中文").getBytes("GBK"));

		
		//經過改方法指定文件名,上傳,經過指定dir目錄 , 能夠直接上傳到目錄裏面 重複上傳,文件裏面是直接累加。 不會從新寫文件!
		// store操做,會從新執行會覆蓋原有文件
		boolean result = ftpClient.storeFile("upload-store.txt", bais);
		bais.close();
		assertTrue(result);

		 //方式二
		ftpClient.deleteFile("upload-store2.txt");
		OutputStream ops = ftpClient.storeFileStream("upload-store2.txt");
		ops.write(new String("中文").getBytes("GBK"));
		ops.flush();
		ops.close();

		assertEquals(new Integer(1), new Integer(reConnectFtpClient().listFiles("upload-store2.txt").length));

	}

	/**
	 * 上傳excel,圖片這類文件,要設置FileType爲FTPClient. BINARY_FILE_TYPE 不然文件損壞
	 */
	@Test
	public void testStoreBinaryFile() throws Exception {
		ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);

		InputStream ips = this.getClass().getClassLoader().getResourceAsStream("upload/rober.c.martin.jpg");
		assertNotNull(ips);

		//上傳圖片
		boolean result = ftpClient.storeFile("rober.c.martin.jpg", ips);
		assertTrue(result);
		ips.close();

		ips = this.getClass().getClassLoader().getResourceAsStream("upload/excel.xlsx");
		assertNotNull(ips);

		 // 上傳excel , 能夠經過轉換跟建立dir方法,直接指定上傳的目錄
		assertEquals("/", ftpClient.printWorkingDirectory());
		ftpClient.makeDirectory("excel");
		ftpClient.changeWorkingDirectory("excel");

		result = ftpClient.storeFile("excel.xlsx", ips);
		assertTrue(result);
		ips.close();
	}

	@Test
	public void testUploadZhNameFile() throws Exception {

		ftpClient.setCharset(Charset.forName("utf-8"));

		ByteArrayInputStream bais = new ByteArrayInputStream(new String("中文").getBytes());

		boolean result = ftpClient.storeFile(new String("測試中文名.txt".getBytes(), "iso8859-1"), bais);
		assertTrue(result);

		FTPFile[] files = ftpClient.listFiles(new String("測試中文名.txt".getBytes(), "iso8859-1"));
		assertEquals(new Integer(1), new Integer(files.length));

		ftpClient.deleteFile(new String("測試中文名.txt".getBytes(), "iso8859-1"));
	}

	/**
	 * 下載文件
	 */
	@Test
	public void testRetrieveFile() throws Exception {
		String text = "text";

		boolean result = ftpClient.storeFile("upload-store.txt", new ByteArrayInputStream(new String(text).getBytes()));
		assertTrue(result);

		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		result = ftpClient.retrieveFile("upload-store.txt", baos);
		assertTrue(result);

		assertEquals(text, new String(baos.toByteArray()));
		baos.close();

		//方式二
		OutputStream ops = ftpClient.storeFileStream("upload-store.txt");
		byte[] bytes = new byte[text.length()];
		ops.write(bytes);
		ops.flush();
		ops.close();
		assertEquals(text, new String(text));
	}

	/**
	 * 刪除服務器文件
	 */
	@Test
	public void testDeleteFile() throws Exception {

		boolean result = ftpClient.storeFile("upload-store.txt", new ByteArrayInputStream(new String("text").getBytes()));

		 //上傳成功
		assertTrue(result);

		result = ftpClient.deleteFile("upload-store.txt");

		// 判斷刪除成功
		assertTrue(result);
	}

	/**
	 * 目錄間跳轉
	 */
	@Test
	public void testDirApi() throws Exception {
		boolean result;

		assertEquals("/", ftpClient.printWorkingDirectory());

		 //返回boolean 值,若是已經存在 ,getReplyCode爲550碼,並返回false值
		ftpClient.makeDirectory("test-dir-1");
		ftpClient.makeDirectory("test-dir-1/test-dir-11");

		 // 若是test-dir-2這一層目錄沒有,不能一次建立幾層的目錄
		result = ftpClient.makeDirectory("test-dir-2/test-dir-2");
		assertFalse(result);

		 // changeWorkingDirectory 有相應目錄返回true,無相應目錄返回false
		result = ftpClient.changeWorkingDirectory("test-dir-1");
		assertTrue(result);
		assertEquals("/test-dir-1", ftpClient.printWorkingDirectory());

		result = ftpClient.changeWorkingDirectory("invalid-dir");
		assertFalse(result);

		 // 返回上一級
		ftpClient.changeToParentDirectory();
		assertEquals("/", ftpClient.printWorkingDirectory());

		 // 刪除一個目錄 ,若是有子目錄或有子文件,則刪除不成功,須要遞歸刪除
		result = ftpClient.makeDirectory("test-remove-dir");
		assertTrue(result);
		result = ftpClient.removeDirectory("test-remove-dir");
		assertTrue(result);
	}

	@Test
	public void testListFiles() throws Exception {
		ftpClient.storeFile("test-list-files.txt", new ByteArrayInputStream("恆拓開源".getBytes()));

		FTPFile[] ftpFiles = ftpClient.listFiles();
		assertTrue(ftpFiles.length > 0);

		for (FTPFile each : ftpFiles) {
			System.out.println("######################################");
			System.out.println(each.getUser());
			System.out.println(each.getGroup());
			System.out.println(each.getLink());
			System.out.println(each.getRawListing());
			System.out.println("isFile : " + each.isFile());
			System.out.println("type : " + each.getType());
			System.out.println("######################################");
		}

		ftpClient.makeDirectory("test-list-file-dir");
		ftpClient.storeFile("test-list-file-dir/list.txt", new ByteArrayInputStream("fuck".getBytes()));

		//list 傳入一個參數,指定 list改目錄
		ftpFiles = ftpClient.listFiles("test-list-file-dir");
		assertEquals(new Integer(1), new Integer(ftpFiles.length));

		 // 清場工做
		ftpClient.deleteFile("test-list-files.txt");
		ftpClient.deleteFile("test-list-file-dir/list.txt");
		ftpClient.removeDirectory("test-list-file-dir");
	}

	/**
	 * 測試limit一個大小
	 */
	@Test
	public void testListFilesLimitSize() throws Exception {

		ftpClient.makeDirectory("test-list-files-limit");
		ftpClient.storeFile("test-list-files-limit/test-list-files-limit-size-1.txt", new ByteArrayInputStream("恆拓開源".getBytes()));
		ftpClient.storeFile("test-list-files-limit/test-list-files-limit-size-2.txt", new ByteArrayInputStream("恆拓開源".getBytes()));
		ftpClient.storeFile("test-list-files-limit/test-list-files-limit-size-3.txt", new ByteArrayInputStream("恆拓開源".getBytes()));

		FTPListParseEngine engine = ftpClient.initiateListParsing("test-list-files-limit");
		FTPFile[] files = engine.getNext(2);
		assertEquals(new Integer(2), new Integer(files.length));

		files = engine.getNext(1);
		assertEquals(new Integer(1), new Integer(files.length));

		ftpClient.deleteFile("test-list-files-limit/test-list-files-limit-size-1.txt");
		ftpClient.deleteFile("test-list-files-limit/test-list-files-limit-size-2.txt");
		ftpClient.deleteFile("test-list-files-limit/test-list-files-limit-size-3.txt");
		ftpClient.removeDirectory("test-list-files-limit");
	}

}
經過閱讀API,有個Util類跟CopyStreamListener類結合能夠在上傳下載流傳輸的時候對字節流進行回調監控。一樣作一個測試


src/test/java/com/fengzidm/spike/ftp/TestCopyStreamListener.java


package com.fengzidm.spike.ftp;

import static org.junit.Assert.*;

import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.commons.net.io.CopyStreamEvent;
import org.apache.commons.net.io.CopyStreamListener;
import org.apache.commons.net.io.Util;
import org.junit.After;
import org.junit.Test;

/**
 * 經過CopyStreamListener,能夠有事件監聽目標流讀書的大小
 */
public class TestCopyStreamListener extends FtpClientTestCase
{

	private CopyStreamListener listener;

	private String FILE_PATH = "test-copy-stream-listener-upload.txt";

	@Override
	public void setUp() throws Exception
	{
		super.setUp();

		listener = new CopyStreamListener()
		{
			@Override
			public void bytesTransferred( long totalBytesTransferred, int bytesTransferred, long streamSize )
			{
				System.out.println( "#############################" );
				System.out.println( "totalBytesTransferred:" + totalBytesTransferred );
				System.out.println( "bytesTransferred:" + bytesTransferred );
				System.out.println( "streamSize:" + streamSize );
				System.out.println( "#############################" );
			}

			@Override
			public void bytesTransferred( CopyStreamEvent event )
			{
				System.out.println( "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" );
				System.out.println( "getBytesTransferred:" + event.getBytesTransferred() );
				System.out.println( "getStreamSize:" + event.getStreamSize() );
				System.out.println( "getTotalBytesTransferred:" + event.getTotalBytesTransferred() );
				System.out.println( "getSource:" + event.getSource() );
				System.out.println( "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" );
			}
		};

	}

	/**
	 * 上傳文件
	 */
	@Test
	public void testStoreFile() throws Exception
	{
		String text = "恆拓開源";
		ByteArrayInputStream bais = new ByteArrayInputStream( new String( text ).getBytes() );
		OutputStream ops = ftpClient.storeFileStream( FILE_PATH );
		Long len = Util.copyStream( bais, ops, 2, text.getBytes().length, listener, true );
		
		// 必定要close
		ops.close();
		bais.close();

		assertEquals( new Long( text.getBytes().length ), len );

	}

	@Test
	public void testRetriveFile() throws Exception
	{
		String text = "恆拓開源";
		ftpClient.storeFile( FILE_PATH, new ByteArrayInputStream( text.getBytes() ) );

		InputStream ips = ftpClient.retrieveFileStream( FILE_PATH );
		BufferedOutputStream bops = new BufferedOutputStream( new ByteArrayOutputStream() );
		Long len = Util.copyStream( ips, bops, 5, text.getBytes().length, listener );
		
		bops.close();
		ips.close();
		
		assertEquals( new Long( text.getBytes().length ), len );
	}

	@After
	public void tearDown() throws Exception
	{
		ftpClient.deleteFile( FILE_PATH );

		super.tearDown();
	}

}
而後我的總結簡單列舉一下API



FTPClient

鏈接

void connect(String hostname,int port)  

關閉鏈接

void disconnect()

進行登陸

boolean login(String username, String password)

退出登陸

boolean logout()

設置超時

void setConnectTimeout()

設置命令編碼

void setControlEncoding(String encoding); 

上傳文件,若是文件已經存在,追加文件內容

boolean appendFile(String remote, InputStream local)

OutputStream appendFileStream(String remote)

上傳文件,若是文件已經存在,從新覆蓋其內容

boolean storeFile(String remote, InputStream local)

OutputStream storeFileStream(String remote)

刪除文件

boolean deleteFile(String pathname)

下載文件

boolean retrieveFile(String remote, OutputStream local)

InputStream   retrieveFileStream(String remote)

重命名(移動)文件

boolean rename(String from, String to)

文件列表

FTPFile[] listFiles()

FTPFile[] listFiles(String pathname)

FTPFile[] listFiles(String pathname, FTPFileFilter filter)

目錄列表

FTPFile[] listDirectories()

FTPFile[] listDirectories(String parent)

建立目錄

boolean makeDirectory(String pathname)

刪除目錄(弱有子目錄或文件,執行返回false,需遞歸刪除)

boolean removeDirectory(String pathname)

獲取當前工做目錄

String printWorkingDirectory()

改變工做目錄

boolean changeToParentDirectory()

boolean changeWorkingDirectory(String pathname)

設置文件類型 默認爲ASCII 上傳圖片excel等要設置Binary類型

boolean setFileType(int fileType)

設置爲主動工做模式

void enterLocalActiveMode()

設置爲被動工做模式

void enterLocalPassiveMode()

得到響應碼

int   getReplyCode()

得到響應信息

String     getReplyString()

String[] getReplyStrings()

 

 

FTPClientConfig

//設置服務端語言(「zh,da,fr這些」)

void  setServerLanguageCode(String serverLanguageCode)

//設置日期轉換格式

void  setRecentDateFormatStr(String recentDateFormatStr)

void  setDefaultDateFormatStr(String defaultDateFormatStr)

FTPClient f=FTPClient();

   FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);

   conf.setServerLanguageCode("fr");

   f.configure(conf);

 

FTPListParseEngine 用於list**操做時能夠指定如分頁大小之類信息

FTPClient f=FTPClient();

    f.connect(server);

    f.login(username, password);

    FTPListParseEngine engine = f.initiateListParsing(directory);

 

    while (engine.hasNext()) {

       FTPFile[] files = engine.getNext(25);  // "page size" you want

       //do whatever you want with these files, display them, etc.

       //expensive FTPFile objects not created until needed.

    }


最後:

這裏使用到只是簡單FTPClient , 在commons.net.ftp包中,還有像FTPHTTPClient , FTPSClient , 這些類你們應該琢磨一下都知道是幹麻用的啦^-^。

相關文章
相關標籤/搜索