2008-09-25 112 views
28

我一直在寫一個小應用程序,可以讓人們上傳&下載文件給我。我已經爲這個應用添加了一個Web服務來提供上傳/下載功能,但我並不太確定我的實現如何處理大文件。Web服務可以返回流嗎?

目前的上傳下載&方法的定義是這樣的(使用Apache CXF的書面):

boolean uploadFile(@WebParam(name = "username") String username, 
    @WebParam(name = "password") String password, 
    @WebParam(name = "filename") String filename, 
    @WebParam(name = "fileContents") byte[] fileContents) 
    throws UploadException, LoginException; 

byte[] downloadFile(@WebParam(name = "username") String username, 
    @WebParam(name = "password") String password, 
    @WebParam(name = "filename") String filename) throws DownloadException, 
    LoginException; 

所以文件被上傳和下載爲一個字節數組。但是,如果我有一個愚蠢的大小(例如1GB)的文件,它肯定會嘗試將所有這些信息存入內存並使我的服務崩潰。

所以我的問題是 - 是否有可能返回某種類型的流?我想這不會是非常獨立的操作系統,但。儘管我知道Web服務背後的理論,但實際方面仍然需要我提供一些信息。

乾杯任何輸入, 李

+0

爲什麼5年前的問題再次激活的任何具體原因?答案已過時? – Loko 2013-12-20 00:06:10

回答

6

Stephen Denne擁有地鐵的實施,滿足您的要求。經過短暫的分析,我的答案在下面提供,爲什麼是這種情況。

大多數使用HTTP作爲消息協議構建的Web服務實現都是REST兼容的,因爲它們只允許簡單的發送 - 接收模式,而不再是其他。這大大提高了互操作性,因爲所有各種平臺都可以理解這個簡單的體系結構(例如,與.NET Web服務交談的Java Web服務)。

如果你想保持這個你可以提供分塊。

boolean uploadFile(String username, String password, String fileName, int currentChunk, int totalChunks, byte[] chunk); 

這將需要在情況下,一些地方的步法你沒有得到正確的順序塊(或者你可以只需要塊來以正確的順序),但它很可能是很容易實現。

+3

從概念上說,你是對的,但是肯定有解決這個限制的方法(就像@spdenne提到的那樣)。一個好的解決方案是使用MTOM。 – 2009-01-30 11:46:27

+0

這只是不正確的。你*可以*寫一個流式web服務。實際上,HttpServletResponse有一個名爲getOutputStream()的方法。 – nont 2011-11-11 15:22:54

0

這樣做的一種方法是添加一個uploadFileChunk(byte [] chunkData,int size,int offset,int totalSize)方法(或類似的東西),上傳文件的一部分,服務器將它寫入到磁盤。

14

是的,這可能與地鐵。請參閱Large Attachments示例,該示例看起來像是您想要的。

JAX-WS RI支持以流方式發送和接收大型附件。

  • 在編程模型中使用MTOM和DataHandler。
  • 將DataHandler轉換爲StreamingDataHandler並使用其方法。
  • 請確保您調用StreamingDataHandler.close()並關閉StreamingDataHandler.readOnce()流。
  • 在客戶端啓用HTTP分塊。
3

當你使用一個標準化的Web服務發送者和reciever不依賴於XML數據的完整性從一方向另一方發出。這意味着只有在發送最後一個標記時才完成Web服務請求和回答。考慮到這一點,Web服務不能被視爲流。

這是合乎邏輯的,因爲標準化的Web服務確實依賴於http協議。那個是「無狀態的」,會說它像「開放連接...發送請求...接收數據...關閉請求」一樣工作。無論如何,連接將在最後關閉。所以像流媒體這樣的東西並不打算在這裏使用。或者他位於http上方(如Web服務)。

非常抱歉,但據我所知,在Web服務中不可能有流式傳輸。更糟糕的是:根據Web服務的實現/配置,byte [] - 數據可能會轉換爲Base64而不是CDATA標籤,並且請求可能會變得更加臃腫。

P.S .:是的,正如其他人寫道,「chuinking」是可能的。但是,這不是流式;-) - 無論如何,它可以幫助你。

0

請記住,Web服務請求基本歸結爲一個HTTP POST。

如果您在.NET中查看.ASMX文件的輸出,它將顯示POST請求和響應的樣子。

正如@Guvante所說,分塊將成爲您想要的最接近的東西。

我想你可以實現自己的Web客戶端代碼來處理TCP/IP並將任何東西傳輸到應用程序中,但至少可以說這很複雜。

0

我認爲使用簡單的servlet來完成這個任務會更容易一些,或者有什麼理由不能使用servlet嗎?

例如,您可以使用Commons開放源代碼庫。

0

Java的RMIIO庫提供了跨RMI交付RemoteInputStream - 我們只需要RMI,但您應該能夠使代碼適應其他類型的RMI。這可能對您有所幫助 - 特別是如果您可以在用戶端使用小型應用程序。該庫的開發目的是爲了能夠限制推送到服務器的數據的大小,以避免您描述的情況類型 - 實際上是通過填充內存或磁盤來實現DOS攻擊。

利用RMIIO庫,服務器端可以決定它願意提取多少數據,以及HTTP PUT和POST的位置,客戶端可以做出決定,包括推送的速率。

1

對於WCF我認爲它有可能在消息上定義一個成員作爲流,並適當地設置綁定 - 我已經看到這個工作與wcf與Java web服務交談。

您需要在httpTransport配置中設置transferMode =「StreamedResponse」,並使用mtomMessageEncoding(需要在配置中使用自定義綁定部分)。

我認爲一個限制是你只能有一個消息體成員,如果你想流(哪種有道理)。

0

是的,一個web服務可以做流媒體。我使用Apache Axis2和MTOM創建了一個web服務,以支持從XML中呈現PDF文檔。由於生成的文件可能非常大,因此我們不想將所有文件保存在內存中,因此流式傳輸非常重要。看看Oracle的文檔streaming SOAP attachments.

或者,你可以自己做,而tomcat將創建塊化頭。這是流式傳輸的彈簧控制器功能的一個例子。

@RequestMapping(value = "/stream") 
     public void hellostreamer(HttpServletRequest request, HttpServletResponse response) throws CopyStreamException, IOException 
{ 

      response.setContentType("text/xml"); 
      OutputStreamWriter writer = new OutputStreamWriter (response.getOutputStream()); 
      writer.write("this is streaming"); 
      writer.close(); 

    } 
1

我不想把它分解給那些認爲流式web服務不可行的人,但實際上所有http請求都是基於流的。每個瀏覽器執行GET操作都是基於流的。每個對Web服務的調用都是基於流的。是的,所有。我們沒有注意到我們正在實現服務或頁面的級別,因爲較低級別的架構正在爲您處理這個問題 - 但它正在完成。

你有沒有在瀏覽器中注意到有時可能需要一段時間才能獲取頁面 - 瀏覽器只是不停地顯示沙漏?這是因爲瀏覽器正在等待一個流。

流是mime/types必須在實際數據之前發送的原因 - 它只是一個字節流到瀏覽器,如果你沒有告訴它它是什麼,它將無法識別照片第一。這也是爲什麼你必須在發送之前傳遞一個二進制文件的大小 - 瀏覽器將無法知道圖像停止的位置,頁面會再次出現。

這只是一個字節流到客戶端。如果你想爲自己證明這一點,只需在處理請求的任何時刻保持輸出流並關閉()它。你會炸掉一切。瀏覽器將立即停止顯示沙漏,並將顯示「無法找到」或「在服務器上重置連接」或其他此類消息。

很多人不知道所有這些東西都是基於流的,顯示了它上面有多少東西被分層。有些人會說太多東西 - 我就是其中之一。

祝你好運,快樂發展 - 放鬆肩膀!

0

實際上,「處理TCP/IP並將任何東西流入應用程序」並不難。試試這個......

class MyServlet extends HttpServlet 
{ 
    public void doGet(HttpServletRequest request, HttpServletResponse response) 
    { 
     response.getOutputStream().println("Hello World!"); 
    } 
} 

這就是它的全部。在上面的代碼中,您已經對從瀏覽器發送的HTTP GET請求做出了響應,並將文本「Hello World!」返回給該瀏覽器。

請記住,「Hello World!」不是有效的HTML,所以你最終可能會在瀏覽器中出現錯誤,但這確實就是它的全部。

祝您好運!

Rodney