BeanSoft's Java Blog
免费电子书/视频《MyEclipse 6 Java 开发中文教程》作者刘长炯官方博客

上周给某单位做了一次开发培训, 学员们希望学习用FTP上传下载的Java实现. 上网找了下, 最后找到了两个, 一个是 Apache的Jakarta Commons Net来实现, 其设置选项还是挺多的, 网上的资料也很多, Google一下一大把, 另一个则是http://www.enterprisedt.com/ 开发的一个开源和商业版本的FTP类库, 商业版本支持批量目录的上传和下载. 用的过程中发现了中文问题, 不过最后还是胜利解决了.

    先说一下搭建测试FTP服务器, 一般Windows下用的多的有Server-U(收费)等, 开源的有FileZilla FTP Server(经测试貌似无法上传超过100MB的文件, 不知道哪里有设置, 最后否定了), 目前使用的是一款免费绿色小巧的FTP服务器来做测试: TYPSoft FTP Server. 下载后直接解压缩即可运行, 不过如果要显示中文界面的话, 请修改config.ini:

LangFile=chineses

之后建立用户进行测试就可以了. 界面如下所示:

image

 

一般的客户端连接中文Windows下的FTP服务器, 默认编码是GB2312, 因此不加设置的话很容易无法上传和下载中文附件. 网上有一些代码片段讨论Jakarta Commons Net, 但是看起来正确的解决此问题的代码不多. 其实FtpClient类已经提供了设置的方法, 调用:

ftpClient.setControlEncoding("gb2312");

即可, 这样在打开Socket的时候都会才用正确的reader和writer了. 相关的源码片段如下:

/**
 * Sets the character encoding used by the FTP control connection.
 * Some FTP servers require that commands be issued in a non-ASCII
 * encoding like UTF-8 so that filenames with multi-byte character
 * representations (e.g, Big 8) can be specified.
 *
 * @param encoding The new character encoding for the control connection.
 */
public void setControlEncoding(String encoding) {
    _controlEncoding = encoding;
}


/**
 * @return The character encoding used to communicate over the
 * control connection.
 */
public String getControlEncoding() {
    return _controlEncoding;
}

 

下面要说的是edtftpj, 去其官方网站下载得到ZIP, 解压缩后即可运行其自带的例子, 不过默认清空下不支持汉字. 例子及压缩包内容如下图所示:

image

可见支持的功能还是挺全面的, 要看的例子就是upload_download_and_delete_a_file, 现在新建一个Java项目, 把lib\edtftpj.jar加入项目即可,然后将例子复制进来编译运行, 如下所示:

import com.enterprisedt.net.ftp.FileTransferClient;
import com.enterprisedt.util.debug.Level;
import com.enterprisedt.util.debug.Logger;
import java.io.File;

public class UploadDownloadFiles {

    public static void main(String[] args) {

        // we want remote host, user name and password
        if (args.length < 3) {
            System.out
                    .println("Usage: run remote-host username password");
            System.exit(1);
        }

        // extract command-line arguments
        String host = args[0];
        String username = args[1];
        String password = args[2];
        String filename = "UploadDownloadFiles.java";

        // set up logger so that we get some output
        Logger log = Logger.getLogger(UploadDownloadFiles.class);
        Logger.setLevel(Level.INFO);

        FileTransferClient ftp = null;

        try {
            // create client
            log.info("Creating FTP client");
            ftp = new FileTransferClient();

            // set remote host
            ftp.setRemoteHost(host);
            ftp.setUserName(username);
            ftp.setPassword(password);

            // connect to the server
            log.info("Connecting to server " + host);
            ftp.connect();
            log.info("Connected and logged in to server " + host);

            log.info("Uploading file");
            ftp.uploadFile(filename, filename);
            log.info("File uploaded");

            log.info("Downloading file");
            ftp.downloadFile(filename + ".copy", filename);
            log.info("File downloaded");

            log.info("Deleting remote file");
            ftp.deleteFile(filename);
            log.info("Deleted remote file");

            File file = new File(filename + ".copy");
            file.delete();
            log.info("Deleted local file copy");

            // Shut down client
            log.info("Quitting client");
            ftp.disconnect();

            log.info("Example complete");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
具体操作包括上传,下载,不过当文件名为中文时候, 上传和下载都会出现问题, 报错提示服务器找不到文件. 最终解决方法是继承FileTransferClient,然后获取连接时的配置信息然后修改交互时的字符集:
import com.enterprisedt.net.ftp.FileTransferClient;

/**
 * 可以设置连接时的字符集的FTP客户端.
 * @author BeanSoft
 * 2008-11
 */
public class SetEncodingFileTransferClient extends FileTransferClient {
    /**
     * 设置连接时的字符集, 默认值是US-ASCII.
     * @param controlEncoding 字符集名, 如GB2312等
     */
    public synchronized void setControlEncoding(String controlEncoding) {
        super.masterContext.setControlEncoding(controlEncoding);
   }
}

相应的测试代码是:

import java.io.File;

import com.enterprisedt.util.debug.Level;
import com.enterprisedt.util.debug.Logger;

public class UploadDownloadFiles {

    public static void main(String[] args) {
        // extract command-line arguments
        String host = "localhost";
        String username = "test";
        String password = "test";
        String filename = "图片输出.gif";

        // set up logger so that we get some output
        Logger log = Logger.getLogger(UploadDownloadFiles.class);
        Logger.setLevel(Level.INFO);

        SetEncodingFileTransferClient ftp = null;

        try {
            // create client
            log.info("Creating FTP client");
            ftp = new SetEncodingFileTransferClient();

            // set remote host
            ftp.setRemoteHost(host);
            ftp.setUserName(username);
            ftp.setPassword(password);
            ftp.setControlEncoding("GB2312");

            // connect to the server
            log.info("Connecting to server " + host);
            ftp.connect();
            log.info("Connected and logged in to server " + host);

            log.info("Uploading file");
            ftp.uploadFile(filename, filename);
            log.info("File uploaded");

            log.info("Downloading file");
            ftp.downloadFile(filename + ".copy", filename);
            log.info("File downloaded");

            log.info("Deleting remote file");
            //ftp.deleteFile(filename);
            log.info("Deleted remote file");

            File file = new File(filename + ".copy");
           // file.delete();
            log.info("Deleted local file copy");

            // Shut down client
            log.info("Quitting client");
            ftp.disconnect();

            log.info("Example complete");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

运行后服务器可看到正确的文件名, 而本机则可以下载到正确的文件副本.

输出日志为:

INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.203 : Creating FTP client
INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.359 : Connecting to server localhost
INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.546 : Connected and logged in to server localhost
INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.546 : Uploading file
INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.703 : File uploaded
INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.703 : Downloading file
INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.734 : File downloaded
INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.734 : Deleting remote file
INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.734 : Deleted remote file
INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.734 : Deleted local file copy
INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.734 : Quitting client
INFO [ftp.UploadDownloadFiles] 26 十一月 2008 21:02:48.734 : Example complete

运行后一切正常, 非常好. 而此软件包的付费版本可支持目录批量上传和下载(Apache Commons Net 貌似不支持), 当然了, 许可证就要自己想办法Crack了:

import com.enterprisedt.net.ftp.FTPClient;
import com.enterprisedt.net.ftp.pro.ProFTPClient;
import com.enterprisedt.util.debug.Level;
import com.enterprisedt.util.debug.Logger;
import java.io.File;

public class TransferMultipleFilesDirectories {

    public static void main(String[] args) {

        // we want remote host, user name and password
        if (args.length < 5) {
            System.out
                    .println("Usage: run remote-host username password localdir remotedir");
            System.exit(1);
        }

        // extract command-line arguments
        String host = args[0];
        String username = args[1];
        String password = args[2];
        String localDir = args[3];
        String remoteDir = args[4];

        // set up logger so that we get some output
        Logger log = Logger.getLogger(TransferMultipleFilesDirectories.class);
        Logger.setLevel(Level.DEBUG);

        ProFTPClient ftp = null;

        try {
            // create client
            log.info("Creating FTP client");
            ftp = new ProFTPClient();

            // set remote host
            log.info("Setting remote host");
            ftp.setRemoteHost(host);

            // connect to the server
            log.info("Connecting to server " + host);
            ftp.connect();
            log.info("Connected to server " + host);

            // log in
            log.info("Logging in with username=" + username + " and password="
                    + password);
            ftp.login(username, password);
            log.info("Logged in");

            log.info("Uploading directory");
            ftp.mput(localDir, remoteDir, "*.html", true);
            log.info("Directory uploaded");
            
            log.info("Downloading directory");
            ftp.mget(localDir + ".copy", remoteDir, "*.html", true);
            log.info("Directory downloaded");
            
            log.info("Deleting remote directory");
            ftp.rmdir(remoteDir, true);
            log.info("Remote directory deleted");

            // Shut down client
            log.info("Quitting client");
            ftp.quit();

            log.info("Example complete");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

至此, 我们的任务已经完成, 可以加上定时器之类的软件或者类库实现定时同步/备份文件等功能. 想获取本项目源代码? 请点击 http://cid-519b3f7aa2172030.skydrive.live.com/self.aspx/java/opensource/javaftp.zip 138KB 下载(单线程下载, 请不要用下载软件如迅雷).

posted on 2008-11-26 20:57 BeanSoft 阅读(1643) 评论(6)  编辑  收藏 所属分类: Open Source World
Comments
  • # re: 用开源软件edtftpj实现FTP文件上传下载,无中文问题
    Always BaNg.
    Posted @ 2008-11-26 23:05
    其实FTP协议还是非常简单的,教学直接使用java socket实现一个简单的岂不是更好?

    惊叹你知识面的广泛,blog上资料的海量。pfpf!

    我也有时候给客户做Eclipse plugin/gef/gmf培训,能否交个朋友?我给你发了一封邮件。

      回复  更多评论   
  • # re: 用开源软件edtftpj实现FTP文件上传下载,无中文问题
    Robin's Java World
    Posted @ 2008-11-26 23:15
    这东东List Solaris系统FTP服务时有Bug.  回复  更多评论   
  • # re: 用开源软件edtftpj实现FTP文件上传下载,无中文问题[未登录]
    beansoft
    Posted @ 2008-11-27 10:16
    已收到, 嗯,暂时还没试过其他FTP服务器。估计Apache的可能做的更兼容一些吧。  回复  更多评论   
  • # re: 用开源软件edtftpj实现FTP文件上传下载,无中文问题
    BeanSoft
    Posted @ 2008-11-27 10:20
    @Always BaNg.
    谈不上海量广泛 很杂倒是真的 搞开发一般都这样 什么都得了解一点  回复  更多评论   
  • # re: 用开源软件edtftpj实现FTP文件上传下载,无中文问题
    枫之临界
    Posted @ 2008-11-27 21:14
    我下载不了那个附件,可以发到我的邮箱吗?  回复  更多评论   
  • # re: 用开源软件edtftpj实现FTP文件上传下载,无中文问题
    枫之临界
    Posted @ 2008-11-27 21:14
    对了,忘了把邮箱说一下,,呵
    E-mail:yytime@foxmail.com  回复  更多评论   

标题  
姓名  
主页
验证码 *  
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-11-26 21:05 编辑过
 
 

推荐图书:
走出软件作坊》、《悟透JavaScript》、《Head First 设计模式
相关链接:
网站导航: