当前位置:   article > 正文

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

ftpclient.retrievefile

由于项目有个需求是要去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);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
第二小步就是获取ftp上的文件:
    FTPFile[] ftpFIles = ftpClient.listFiles();
  • 1

第二步:解析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;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

第三步就是将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());//删除文件
  • 1
  • 2
  • 3
  • 4
  • 5

坑一: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();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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

小结:

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

1、ftpClient.retrieveFileStream(String remote);

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

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

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

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

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

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

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

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();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/255556
推荐阅读
相关标签
  

闽ICP备14008679号