记一次FTPClient的使用。(复制、删除、移动)

由于项目有个需求是要去ftp服务器上取文件,文件为xml格式,解析后定时抽取入库。
一开始分了三步,第一步取文件,第二步解析,第三步抽取入库。后来做完后又要根据将文件抽取入库失败的移动到error目录上。所以最后大致上可以分成四步。由于定时器可以利用spring+quartz实现,用到的是spring配置文件,所以就不记录了。

在刚开始动手时,由于没搭建好ftp服务器测试,所以就先做了第二步xml的解析。开始时将解析方法声明为parseXML(File file),将文件放到了本地磁盘上测试,通过。然后开始有点坑的就来了,当搭建好ftp服务器后,就尝试第一步,ftp上取文件。就去调用 commons.net上的FTPClient。

第一步,ftp服务器上取文件。

第一小步,肯定就是连接上ftp服务器,度娘,连接ftp如下:
public void connectServer(String ip, int port, String userName, String userPwd) {
        ftpClient = new FTPClient();
        try {
            int reply; 
            // 连接
            ftpClient.connect(ip, port);
            // 登录
            ftpClient.login(userName, userPwd);
            reply = ftpClient.getReplyCode(); 

        } catch (Exception e) {
            log.error("ftp链接失败", e);
        }
    }
第二小步就是获取ftp上的文件:
    FTPFile[] ftpFIles = ftpClient.listFiles();

第二步:解析xml文件

文件取到了,当然就是去解析xml了,然后坑爹的就来了,之前定好的方法parseXML(File file),接受的是File文件,然而ftpClient获取下来的文件是FtpFile格式。一开始想着怎么去转,花费了一些时间,可能有转过去的方法,但是我没找到。然后度娘上又给了一个是将FtpFile格式的xml解析的,没办法,就换成了 parseXML(FTPFile fPath)。由于SAXReader里的read方法可以接受inputstream流,所以只要将FtpFile转换成流就ok了,ins = ftpClient.retrieveFileStream(fPath.getName());
这里有个就是每次调用ftpClient.retrieveFileStream()后,需要调用ftpClient.getReply()这个方法,把接下来的226消费掉,如果没有写,下一次ftpClient.retrieveFileStream()返回的InputStream对象会一直为null!!!代码如下:

public List<Map<String, Object>> parseXML(FTPFile fPath) {
        List<Map<String, Object>> parseList = new ArrayList<Map<String, Object>>();

        String rtn = "";
        Document document = null;
        InputStream ins = null;
        try {
            SAXReader saxreader = new SAXReader();
            ins = this.ftpClient.retrieveFileStream(fPath.getName());
            document = saxreader.read(ins);
            Element rootEle = document.getRootElement(); //<dataroot>
            //得到所有的一级子元素
            List firstElements = rootEle.elements(); 
            Iterator it = firstElements.iterator();
            while(it.hasNext()){
                 //依次得到每一个一级子元素
                Element firstElement  = (Element) it.next(); //<YW_LZXX>
                //得到一级子元素下面的所有元素,及其附带值
                List second_Elements=firstElement.elements();
                Iterator second_Element=second_Elements.iterator();
                Map<String, Object> map = new HashMap<String, Object>();
                while(second_Element.hasNext()){
                    Element sec_Element=(Element)second_Element.next();
                    map.put(sec_Element.getName(), sec_Element.getText());
                }
                parseList.add(map);
            }
            ftpClient.getReply();
        } catch (Exception e) {
            log.error("xml解析失败", e);
        } finally {
            if(ins != null){
                try {
                    ins.close();
                } catch (IOException e) {
                    log.error("IO关闭失败", e);
                }
            }
        }
        return parseList;
    }

第三步就是将xml解析后的数据入库了,这个略。

第四步就是要将入库失败的文件进行移动

这一步是在xml解析入库时做的,循环遍历时,先确定一个xml文件里面总入库的数据有多少,然后成功入库的累加,最后判断成功入库数和总入库数是否相等,当成功数小于入库数时,就将该文件进行复制到errors目录,再将原文件删除。

一开始的思想是先将文件转换成流,判断有没有errors目录,有的话切换目录,然后将数据流转化成文件,再把文件删除。
所以一开始的做法如下:

InputStream ins = ftpClient.retrieveFileStream(ftpFile.getName()); //转换成输入流
ftpClient.changeWorkingDirectory("\\errors");//切换目录
ftpClient.storeFile(ftpFile.getName(), ins);//复制文件
ins.close();
ftpClient.deleteFile("/"+ftpFile.getName());//删除文件

坑一:ftpClient.changeWorkingDirectory(String pathname);//切换目录

由于没发现FTPClient有直接判断是否存在某个目录,所以在问度娘的时候,得到了一个巧方法,就是ftpClient.changeWorkingDirectory(“\errors”);根据返回值true或false来判断有没有该目录。为什么说他是坑呢,因为在用的时候,看不到效果,用ftpClient.printWorkingDirectory()看当前的路径时,很多时候发现切换后的路径为null。

坑二:ftpClient.storeFile(String remote, InputStream local);//复制文件

在进行文件复制时,调用该方法后,文件并未复制到指定的目录。没办法,又是度娘,
发现很多都是说未成功是需要设置ftpClient.enterLocalPassiveMode();将其设置为消极模式。具体百度 FTPClient主动模式和被动模式 了解一下区别,但是我发现设置后,还是没用啊,没办法。

坑三:InputStream ins = ftpClient.retrieveFileStream(String remote); //转换成输入流

当进行了该转化后,不管后面的文件有没有复制成功,下次循环时,去解析xml时,得到的inputstream为null,可能就是因为没设置ftpClient.getReply(); 的原因。


因为当时赶着下班要提交,所以第四步的复制移动删除就没做了。没错,别人下班,你还是要加班。作为一个新手,好不容易有点事做了,怎么可以做一半就不做了呢,所以那天晚上加班。

加班嘛,中间隔了一段时间去吃饭,脑子也放空一下,跳出了复制后删除的思维。因为要求是将错误文件移动到指定目录,所以就百度了FTPClient的移动功能,没想到还真有,
ftpClient.rename(String from, String to);//移动文件

至于目录是否存在,直接用ftpClient.makeDirectory(String pathname);//创建目录
存在返回false,不存在就创建。

至此,功能就实现了,但是觉得以后万一遇到复制功能 的呢,所以就又去找度娘一下。并实验了一下,可以用,代码如下:

    ftpClient.setBufferSize(1024); 
    ByteArrayOutputStream fos=new ByteArrayOutputStream();
    ftpClient.retrieveFile("\\"+ftpFile.getName(), fos);
    ByteArrayInputStream in=new ByteArrayInputStream(fos.toByteArray());
    ftpClient.storeFile("\\"+errorDir+"\\"+ftpFile.getName(), in);
    fos.close();
    in.close();

这是将流在内存里转换,实现了复制功能。

小结:

在实现该功能后,个人感觉有几个常用的FTPClient的方法:

1、ftpClient.retrieveFileStream(String remote);

使用该方法后,调用ftpClient.getReply()方法,否则下次调用该方法会返回null;

2、ftpClient.storeFile(String remote, InputStream local);//复制文件

当使用该方法返回true,但是目录没有成功复制文件时,可以设置一下被动模式ftpClient.enterLocalPassiveMode();

3、ftpClient.changeWorkingDirectory(String pathname);//切换目录

此方法可以切换目录。但是个人在用过程中,对其返回值true和false感觉有点怪。

4、ftpClient.rename(String from, String to); //移动文件到新目录

当只是需要移动文件时,可以选择此方法,不必像楼主刚开始时那样,又是判断有没有目录、复制、删除文件。

5、ftpClient.deleteFile(String pathname); //删除文件

6、ftpClient.makeDirectory(String pathname);//创建目录

7、ftpClient.retrieveFile(String remote, OutputStream local)//移动文件

对于复制文件,如果小文件,直接调用ftpClient.storeFile(String remote, InputStream local);可能可以成功,但是如果文件偏大,可能复制就会出问题。所以找了一个利用将文件读到内存的方法http://bbs.csdn.net/topics/390373219,据说16Mb以上的都能复制成功,虽然没去实验,但是基本的复制确实没问题。

ftpClient.setBufferSize(1024); 
ByteArrayOutputStream fos=new ByteArrayOutputStream();
ftpClient.retrieveFile("\\"+ftpFile.getName(), fos);
ByteArrayInputStream in=new ByteArrayInputStream(fos.toByteArray());
ftpClient.storeFile("\\"+errorDir+"\\"+ftpFile.getName(), in);
fos.close();
in.close();

完成后,在本地服务器测试没发现问题。但是别的服务器会出现程序卡死现象,所以在程序开始时设置ftpClient.enterLocalPassiveMode();,这个方法的意思就是每次数据连接之前,ftp client告诉ftp server开通一个端口来传输数据。为什么要这样做呢,因为ftp server可能每次开启不同的端口来传输数据,但是在linux上,由于安全限制,可能某些端口没有开启,所以就出现阻塞。
https://www.cnblogs.com/CopyPaster/p/3494579.html

  • 9
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
使用FTPClient移动文件,首先需要建立一个FTP连接。我们可以使用Apache Commons Net库中的FTPClient类来实现。 以下是实现移动文件的步骤: 1. 创建一个新的FTPClient对象。 2. 使用connect()方法连接到FTP服务器。 3. 使用login()方法登录到FTP服务器。 4. 使用changeWorkingDirectory()方法进入要移动文件的目录。 5. 使用rename()方法将文件从原始位置移动到目标位置。 以下是一个简单的示例代码,演示如何使用FTPClient移动文件: ```java import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; public class FTPExample { public static void main(String[] args) { String server = "ftp.example.com"; int port = 21; String user = "username"; String password = "password"; String originalPath = "/path/to/original/file.txt"; String destinationPath = "/path/to/new/file.txt"; FTPClient ftpClient = new FTPClient(); try { ftpClient.connect(server, port); ftpClient.login(user, password); ftpClient.changeWorkingDirectory("/"); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); boolean success = ftpClient.rename(originalPath, destinationPath); if (success) { System.out.println("文件移动成功!"); } else { System.out.println("文件移动失败!"); } ftpClient.logout(); } catch (Exception e) { e.printStackTrace(); } finally { try { ftpClient.disconnect(); } catch (Exception e) { e.printStackTrace(); } } } } ``` 请注意,以上代码仅作为示例。在实际使用中,您可能需要处理异常、添加适当的错误处理和错误日志录等。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值