职业IT人-IT人生活圈

 找回密码
 成为会员
搜索
查看: 1860|回复: 5

commons-net FTPClient API存取设计

[复制链接]
jinchang 发表于 2011-9-4 09:41 | 显示全部楼层 |阅读模式
文件系统无非就是文件的存取和组织结构。
访问一个文件系统的API也应该是写,读,定位方法(Pathname?/URI?)

FTPClient针对文件的保存和获取各提供了两个方法,分别是:
  
public boolean storeFile(String remote, InputStream local)   
public OutputStream storeFileStream(String remote)   
  
public boolean retrieveFile(String remote, OutputStream local)   
public InputStream retrieveFileStream(String remote)  

public boolean storeFile(String remote, InputStream local)
public OutputStream storeFileStream(String remote)

public boolean retrieveFile(String remote, OutputStream local)
public InputStream retrieveFileStream(String remote)

两个方法貌似相同,实际不同,返回流的那个因为不能马上处理流,所以需要用户手工调用completePendingCommand,而另一个传递流进去的则不需要。可能有同学已经遇到过这个问题了,读写第一个文件时总是正确的,当相同API读写第二个文件时,block住了。这是因为FTPClient要求在进行流操作之后执行completePendingCommand,以确保流处理完毕,因为流处理不是即时的,所以也没有办法不手工调用completePendingCommand。问题是开发者把不返回流的方法末尾加上了completePendingCommand,如果不看代码可能根本不知道。
文档上说:

引用
     * There are a few FTPClient methods that do not complete the
     * entire sequence of FTP commands to complete a transaction.  These
     * commands require some action by the programmer after the reception
     * of a positive intermediate command.  After the programmer's code
     * completes its actions, it must call this method to receive
     * the completion reply from the server and verify the success of the
     * entire transaction.

但是这样仍然还是让人有点困惑,为什么都是存储/读取的方法,有时候要调用completePendingCommand,有时候不调用?更严重的问题是completePendingCommand调用了getReply,如果一个命令通过socket stream传了过去但是没有getReply,即没有completePendingCommand,那么下次发命令时,将会受到本次返回码的干扰,得到无效的响应。而如果在completePendingCommand之后又进行了一次无辜的completePendingCommand,那么因为FTP Server上没有Reply了,就会block。所以completePendingCommand并不是可以随意添加的。

现在出现了两个问题:
1 completePendingCommand很容易多出来或遗漏
2 显式调用completePendingCommand暴露了底层实现,给用户带来不便,用户只想要InputStream或者OutputStream

为了解决这个问题,可以对InputStream进行扩展,建立一个ReplyOnCloseInputStream,如下:
  
private static ReplyOnCloseInputStream extends InputStream{   
  //...   
  public ReplyOnCloseInputStream(InputStream is, FTPClient c){   
    //...   
  }   
  //...   
  @override  
  public void close(){   
    if(c.completePendingCommand){   
      is.close();   
    }else{   
      //throw Exception   
    }   
  }   
}   
//...   
return new ReplyOnCloseInputStream(is, client);  

private static ReplyOnCloseInputStream extends InputStream{
  //...
  public ReplyOnCloseInputStream(InputStream is, FTPClient c){
    //...
  }
  //...
  @override
  public void close(){
    if(c.completePendingCommand){
      is.close();
    }else{
      //throw Exception
    }
  }
}
//...
return new ReplyOnCloseInputStream(is, client);

这样封装之后,FTPClient的用户只需要正常在处理完流之后关闭即可,而不必暴露实现细节。保存文件也可以用相同的方法封装OutputStream。

天上智喜 发表于 2011-9-4 09:42 | 显示全部楼层
呵呵,有什么不同点了

无处不在 发表于 2011-9-4 09:42 | 显示全部楼层
elvishehai 写道
呵呵,有什么不同点了

如果需要对这个FTPClient进行二次封装的话就可以看到不同点了。
比如客户需要接口
  
public interface FileAPI{   
  public InputStream getFile(String pathname);   
  public OutputStream writeFile(String pathname);   
}  

public interface FileAPI{
  public InputStream getFile(String pathname);
  public OutputStream writeFile(String pathname);
}
你没有办法同时把FTPClient也传给用户进行completePendingCommand

走失的猫咪 发表于 2011-9-4 09:42 | 显示全部楼层
同意的楼主的看法。

话说我当年 发表于 2011-9-4 09:42 | 显示全部楼层
哦,应该继承FilterInputStream会更简单和直观

叫我小乖 发表于 2011-9-4 09:42 | 显示全部楼层
很好,终于解决了我困惑的问题
您需要登录后才可以回帖 登录 | 成为会员

本版积分规则

QQ|手机版|小黑屋|网站帮助|职业IT人-IT人生活圈 ( 粤ICP备12053935号-1 )|网站地图
本站文章版权归原发布者及原出处所有。内容为作者个人观点,并不代表本站赞同其观点和对其真实性负责,本站只提供参考并不构成任何投资及应用建议。本站是信息平台,网站上部分文章为转载,并不用于任何商业目的,我们已经尽可能的对作者和来源进行了通告,但是能力有限或疏忽造成漏登,请及时联系我们,我们将根据著作权人的要求立即更正或者删除有关内容。

GMT+8, 2024-4-30 19:19 , Processed in 0.155262 second(s), 20 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表