由于公司给了我一个传FTP的任务,刚好可以学习一下,同时也进行了一些单元测试,spike了一把,哈哈。分享一下。
这里只对最简单常用的FtpClient的上传下载列表功能进行测试学习使用^-^
创建一个maven工程
在工作根目录创建目录
工程path/res/log
工程path/res/ftphome
配置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文件
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 , 这些类大家应该琢磨一下都知道是干麻用的啦^-^。