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

来源:互联网 发布:招聘网站知乎 编辑:程序博客网 时间:2024/06/05 01:45

由于项目有个需求是要去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();
原创粉丝点击