最近项目上需要一个功能:使用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的文件,内容如下:

<?xml version="1.0" encoding="utf-8"?>    "ftp_users">ftpserver.user.admin.userpassword=admin123456\r\nftpserver.user.admin.homedirectory=/mnt/sdcard\r\nftpserver.user.admin.enableflag=true\r\nftpserver.user.admin.writepermission=true\r\nftpserver.user.admin.maxloginnumber=2\r\nftpserver.user.admin.maxloginperip=2\r\nftpserver.user.admin.idletime=3000\r\nftpserver.user.admin.uploadrate=102400\r\nftpserver.user.admin.downloadrate=102400\r\nftpserver.user.anonymous.userpassword=anonymous\r\nftpserver.user.anonymous.homedirectory=/mnt/sdcard\r\nftpserver.user.anonymous.enableflag=true\r\nftpserver.user.anonymous.writepermission=false\r\nftpserver.user.anonymous.maxloginnumber=20\r\nftpserver.user.anonymous.maxloginperip=2\r\nftpserver.user.anonymous.idletime=3000\r\nftpserver.user.anonymous.uploadrate=480000\r\nftpserver.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服务器且监听文件传输状态的代码。很可惜,我没有找到对于文件传输正确性的监听的方法,如果有找到的朋友,请留言指点,谢谢了~~

更多相关文章

  1. GitHub 标星 2.5K+!教你通过玩游戏的方式学习 VIM!
  2. 万字长文带你了解最常用的开源 Squid 代理服务器
  3. 如何在后台运行Linux命令?
  4. 一款常用的 Squid 日志分析工具
  5. No.11 使用firewall配置的防火墙策略的生效模式
  6. Nginx系列教程(一)| 手把手教你在Linux环境下搭建Nginx服务
  7. GitHub 标星 8K+!一款开源替代 ls 的工具你值得拥有!
  8. Nginx系列教程(三)| 一文带你读懂Nginx的负载均衡
  9. RHEL 6 下 DHCP+TFTP+FTP+PXE+Kickstart 实现无人值守安装

随机推荐

  1. Android第二十五课 native程序异常crash
  2. 在Android中使用Kotlin实现发送验证码60
  3. 资料描述Android依赖注入:Google Guice on
  4. Android开发FAQ
  5. [置顶] 在android 4.0 上面移植camera的
  6. 跟Google学写代码:Android运行时权限处理
  7. Android API Guides---App Manifest
  8. Android之Button按钮点击事件的四种方式
  9. Android 完美解决自定义preference与Acti
  10. Android中关于签名的一些知识