2013年8月7日

Java 的檔案傳輸

說到網路通訊,在一般 Java 的實作上多半會兩種方式: HTTP 或 RMI (當然也可寫個 socket server / client)。無論坊間的教育中心或書籍,都會有基本的範例;而且現今的 Internet 中也有很多成熟的 sample code (只要問對谷歌大神)。

那檔案傳輸呢:

一般提到 Java I/O 時,很多的 software developer 並不會對它陌生;遇到 HTTP 的檔案傳輸可使用如 Apache HTTPClient 這樣子的 package,以 multi-part 方式 (MultipartEntity) 來傳輸。

但個人有遇過 "咬" 住的測試經驗。於是乎搬出 PipedInputStream / PipedOutputStream 再加上啟用一個 Thread 來處理,就可以解決這個問題了。以 Groovy 為例:
...
def InputStream pipeOutput(Closure write) {
 def output = new PipedOutputStream()
 def input = new PipedInputStream(output)

 Thread.start {
  try{
   write(output)
   output.flush()

  } finally {
   try { output?.close() } catch (e) {/*nothing to do*/}
  }
 }
 return input
}
...
...
def ctn = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE)
ctn.addPart("zipFile",
 new InputStreamBody(
  pipeOutput { output ->
   Thread.currentThread().sleep(1000)
   inFile = new FileInputStream(fileName)
     output << inFile
  }
  ,'application/x-gzip'
  ,'test.gz'
 )
)
...
...
// HttpClient的處理 <略>
...
@@ (暈了嗎?)
那 RMI 呢? (想到就頭大?)

還好有 RMIIO 這個好心的 package (是 LGPL 哦,感動吧!感謝作者)
它的優點,就是如同它的說明所描述:  makes it as simple as possible to stream large amounts of data
我個人使用它的心得是,沒有 "咬" 住的情形。而且當初看上它的原因是 client code 與 server code 之間,不需要做 nofity 與 ack 這樣多餘的機制來傳檔與送回接到檔案的訊息:
A --- send ---> B
A <--- ack --- B

只要控制是否 throw RemoteException 以及 try / catch 這個 exception 即可。以 Java code 為例:
// 傳送端
// 用壓縮的 InputStream
try {
  ...
  RemoteInputStream remoteIs = exporter.export(new GZIPRemoteInputStream(fileInputStream));
  ...
  remoteObject.receiveData(remoteIs);
  ...
} catch ...
...
// 接收端
public void receiveData(RemoteInputStream remoteIs) throws RemoteException {
  ...
  InputStream is = RemoteInputStreamClient.wrap(remoteIs);
  ...
  if (somethingWrong) throw new RemoteException("some message");
  ...
  // InputStream / OutputStream 後續處理 
  ...
}

沒有留言: