Android搭建Ftp服务器监听文件传输状态

最近项目上需要一个功能:使用Android软件搭建一个FTP服务器,接收文件,然后再使用FTP客户端,将此文件上传到另一个PC服务器上。
给予以上的需求,Android实现FTP客户端的功能已经实现,且网上有很多资料可供参考。所以主要的工作就是搭建这个FTP服务器。恰巧之前实现FTP客户端功能时使用的是Apache的FTP框架,而搭建FTP服务器的功能也可以使用Apache的这个FTP框架,所以直接下载Apache中用于搭建FTP服务器框架的库文件即可。
FTP服务器搭建库文件下载路径

Android搭建Ftp服务器监听文件传输状态_第1张图片
我下载的是1.0.6版本的库文件(第一次下载的最新的1.1.1版本的文件,但是放在项目中后发现没有Ftplet的打包内容,不知道为什么。),如下是我的库文件目录:
Android搭建Ftp服务器监听文件传输状态_第2张图片

注意:需要在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”命令
完成传输命令之后,收到
Android搭建Ftp服务器监听文件传输状态_第3张图片

所以,在对于中途取消传输的操作,可以在这个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服务器且监听文件传输状态的代码。很可惜,我没有找到对于文件传输正确性的监听的方法,如果有找到的朋友,请留言指点,谢谢了~~

你可能感兴趣的:(网络编程,android架构)