最近项目上需要一个功能:使用Android软件搭建一个FTP服务器,接收文件,然后再使用FTP客户端,将此文件上传到另一个PC服务器上。
给予以上的需求,Android实现FTP客户端的功能已经实现,且网上有很多资料可供参考。所以主要的工作就是搭建这个FTP服务器。恰巧之前实现FTP客户端功能时使用的是Apache的FTP框架,而搭建FTP服务器的功能也可以使用Apache的这个FTP框架,所以直接下载Apache中用于搭建FTP服务器框架的库文件即可。
FTP服务器搭建库文件下载路径
我下载的是1.0.6版本的库文件(第一次下载的最新的1.1.1版本的文件,但是放在项目中后发现没有Ftplet的打包内容,不知道为什么。),如下是我的库文件目录:
注意:需要在app的gradle中配置如下内容:
packagingOptions {
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
exclude 'META-INF/DEPENDENCIES'
}
否则,在你编译的时候,会因为这几个库文件中存在重复的以上信息而报错,所以在编译打包的时候去除以上重复的信息。
接下来,就讲述一下Android搭建FTP服务器的过程。
1 . 搭建FTP服务器
1.1 在搭建服务器之前,我们首先定义一下默认的客户端用户名和密码,可以使用String类型的资源文件配置,并将配置写入到一个配置文件中,FTP服务器初始化客户端账户的时候,可以加载该配置文件。
String类型资源文件:在values中创建一个ftpserver_config.xml的文件,内容如下:
"1.0" encoding="utf-8"?>
"ftp_users">
ftpserver.user.admin.userpassword=admin123456\r\n
ftpserver.user.admin.homedirectory=/mnt/sdcard\r\n
ftpserver.user.admin.enableflag=true\r\n
ftpserver.user.admin.writepermission=true\r\n
ftpserver.user.admin.maxloginnumber=2\r\n
ftpserver.user.admin.maxloginperip=2\r\n
ftpserver.user.admin.idletime=3000\r\n
ftpserver.user.admin.uploadrate=102400\r\n
ftpserver.user.admin.downloadrate=102400\r\n
ftpserver.user.anonymous.userpassword=anonymous\r\n
ftpserver.user.anonymous.homedirectory=/mnt/sdcard\r\n
ftpserver.user.anonymous.enableflag=true\r\n
ftpserver.user.anonymous.writepermission=false\r\n
ftpserver.user.anonymous.maxloginnumber=20\r\n
ftpserver.user.anonymous.maxloginperip=2\r\n
ftpserver.user.anonymous.idletime=3000\r\n
ftpserver.user.anonymous.uploadrate=480000\r\n
ftpserver.user.anonymous.downloadrate=480000\r\n
注意:换行符不能省~~
然后在我们的服务器配置类中,加入如下代码,将String类型的内容写入配置文件中:
private void createPropertyFile() {
try {
mPropertyFile = FileUtil.createFile(FileUtil.getRootPath(), "users.properties");
String config = mContext.getString(R.string.ftp_users);
FileOutputStream fos = new FileOutputStream(mPropertyFile);
fos.write(config.getBytes());
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
1.2 启动服务
这个步骤中,需要配置3个工厂模式的类:
a. FtpServerFactory: 设置服务器的配置信息,并启动和停止服务器;
b. PropertiesUserManagerFactory: 配置服务器的用户信息,账户,权限等信息;
c. ListenerFactory:配置服务器的连接监听信息,端口,地址等信息。
启动过程的代码如下:
/**
* 启动ftp服务
*
* @return
*/
public void start() {
if (null != mFtpServer) {
FileUtil.deleteDirAllFiles(mFtpHomePath); // 删除该目录下的所有文件
stop();
}
FtpServerFactory ftpServerFactory = new FtpServerFactory();
try {
// 配置FTP用户信息文件
PropertiesUserManagerFactory userManagerFactory = new PropertiesUserManagerFactory();
userManagerFactory.setFile(mPropertyFile);// 默认账户配置
UserManager userManager = userManagerFactory.createUserManager();
// 账户信息(也可以新增用户)
BaseUser user = (BaseUser) userManager.getUserByName("admin");
user.setHomeDirectory(mFtpHomePath);
user.setPassword("admin123456");
userManager.save(user); // 修改账户信息
ftpServerFactory.setUserManager(userManager);
ListenerFactory listenerFactory = new ListenerFactory();
// replace the default listener
listenerFactory.setPort(Integer.parseInt(GrobalConfig.getConfig(ConfigConstant.FTP_PORT)));
ftpServerFactory.addListener("default", listenerFactory.createListener());
// 配置服务器被操作的命令等回复信息,下面详细介绍
Map ftplets = new HashMap<>();
ftplets.put("miaFtplet",new MyFtpLet());
ftpServerFactory.setFtplets(ftplets);
mFtpServer = ftpServerFactory.createServer();
mFtpServer.start();
} catch (FtpException e) {
e.printStackTrace();
}
}
停止服务:
/**
* 关闭ftp服务
*
* @return
*/
public void stop() {
if (null != mFtpServer && !mFtpServer.isStopped()) {
mFtpServer.stop();
}
mFtpServer = null;
}
2 . 服务器对客户端操作的应答(可用于监听上传或下载文件是否完成,本文只介绍上传)
在DefaultFtplet中提供了很多方法接口,用于在客户端连接、断开、上传下载时返回信息。因此,我们可以从该接口上下手,来监听文件是否上传完成。
要得到接口中返回的内容,我们就需要写一个类来实现该接口,然后复写我们需要得到状态的方法:
public class MyFtpLet extends DefaultFtplet {
@Override
public FtpletResult beforeCommand(FtpSession session, FtpRequest request) throws FtpException, IOException {
return super.beforeCommand(session, request);
}
@Override
public FtpletResult afterCommand(FtpSession session, FtpRequest request, FtpReply reply) throws FtpException, IOException {
return super.afterCommand(session, request, reply);
}
@Override
public FtpletResult onUploadStart(FtpSession session, FtpRequest request) throws FtpException, IOException {
return super.onUploadStart(session, request);
}
@Override
public FtpletResult onUploadEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
return super.onUploadEnd(session, request);
}
}
上面,我只列举了上传文件时,我们需要得到的状态信息。其他更详细的方法以及介绍,请自行查阅Ftplet的api介绍。
onUploadStart:上传开始的状态应答,客户端开启上传过程之前的最后一次应答。
onUploadEnd:上传结束的状态应答,客户端结束上传过程的最后一次应答。
beforeCommand:客户端发过来的命令。如登录命令,连接命令,切换目录命令,上传命令等。
afterCommand:客户端命令执行之后的应答。
FtpSession介绍
session.getConnectionTime()
获取用户连接的时间
session.getFileSystemView()
获取当前服务器的文件结构,返回类型为FtpFile
session.getFileSystemView().getHomeDirectory()
获取服务器根目录路径,返回类型为FtpFile
session.getFileSystemView().getWorkingDirectory()
获取用户当前连接的目录路径,返回类型为FtpFile
session.getFileSystemView().getWorkingDirectory().getName()
获取用户当前连接的目录名字,返回类型为String
session.getFileSystemView().getWorkingDirectory().getAbsolutePath()
获取用户当前连接的目录的完整路径名字,返回类型为String(返回之后最后是没有斜杠的)。用这个可以获取用户文件的存放路径。
session.getLoginTime()
获取用户登录时间(这和连接时间是不同的概念)
FtpRequest介绍
request.getArgument()
获取用户提交的命令中的参数,比如存储命令,这个参数就是文件名
request.getCommand()
获取用户提交的命令中的命令。比如存储的命令是”STOR”
request.getRequestLine()
获取用户提交的命令中的命令+参数
实际上传过程中的命令及应答情况:
开启上传,命令验证成功后:
成功完成传输之后:
所以,如果是正常传输结束,可以在这个判断文件是否传输完成。当然后续的处理还要结合下面这种情况处理。
客户端取消传输之后,除了执行上述传输完成的过程,但还会收到一个”ABOR”命令
所以,在对于中途取消传输的操作,可以在这个afterCommand方法中,对”ABOR”命令做文件上传的取消操作处理,如删除已上传部分的内容等。
补充:
由于Android手机的底层是Linux系统,而该系统限制了小于1024的端口号申请权限。所以如果你的FTP客户端只能连接21端口的FTP服务器,而这时候你又不能开启21端口,那么可以按照如下方法解决:
1. 获取设备系统权限,即root(必须!!!必须!!!必须!!!)。至于root的方法就请自行百度各种root工具吧~~~~
2. 将FTP的端口21映射到你需要监听的端口(该端口号大于1024),代码如下:
private boolean getPermission() {
Process process = null;
DataOutputStream os = null;
try {
process = Runtime.getRuntime().exec("su");
os = new DataOutputStream(process.getOutputStream());
String command = "chmod 777 " + mContext.getPackageCodePath();
os.writeBytes(command + "\n"); // 申请root权限
os.writeBytes("iptables -t nat -A PREROUTING -p tcp --dport 21 -j REDIRECT --to-ports 2121\n"); // 将端口21映射到端口2121上
os.writeBytes("exit\n");
os.flush();
process.waitFor();
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
try {
if (os != null) {
os.close();
}
process.destroy();
} catch (Exception e) {
e.printStackTrace();
}
}
return true;
}
通过上面的操作,你就可以设置监听2121端口,而服务器端21端口和2121端口都可用,接收到的数据都转到2121端口上。这样就解决了Android作为FTP服务器,监听端口号小于1024的需求了!!
以上就是在Android项目中,搭建FTP服务器且监听文件传输状态的代码。很可惜,我没有找到对于文件传输正确性的监听的方法,如果有找到的朋友,请留言指点,谢谢了~~