依據 Jersey 的實作方法,實際上會先在伺服器上把使用者上傳的檔案全部接收、暫存到暫存的資料夾內,然後才會進入使用者定義的 Multi-part 的介面,並獲得代表檔案內容的 FormDataMultiPart。
也就是說實際上如果檔案是要存到伺服器上,伺服器對這樣一個檔案上傳的動作,會花費兩次的磁碟 I/O 時間來接收和儲存這個檔案。
解決方法目前看到的大概就是直接透過 Servlet 接收 request,然後自行處理 Multi-part 的內容。
接收 request 的動作即使透過 Jersey,還是有蠻簡單的方法處理;Multi-part 的部份則可以利用 Apache Commons Streaming API [1-2](Apache Commons FileUpload 的一部分)。
範例如下:
@POST @Path("/upload_file") @Consumes(MediaType.MULTIPART_FORM_DATA) public Response uploadFileByMultiPart(@Context HttpServletRequest request) throws IOException, FileUploadException { Logger log = Logger.getLogger("MultiPartTest"); log.debug("Get request in multipart."); // Create a new file upload handler ServletFileUpload upload = new ServletFileUpload(); InputStream stream = null; // Parse the request FileItemIterator iter = upload.getItemIterator(request); while (iter.hasNext()) { FileItemStream item = iter.next(); String name = item.getFieldName(); if (!item.isFormField()) { log.debug("File field " + name + " with file name " + item.getName() + " detected."); // Process the input stream stream = item.openStream(); // TODO Process the stream..... } } return Response.status(400).build(); }
第 11 行是讓 FileUpload API 去分析 Multi-part 的內容,並且開始做 Multi-part 的列舉
第 19 行就是拿到其中一個 Multi-part 的 body stream,可以開始做接收檔案內容的動作。
不過這裡要特別注意的是,Multi-part 似乎不能跳著拿,也不能同時拿好幾段,一定要照順序跑
所以 TODO 的地方在處理 stream 時,在 stream 處理完以前不能讓程式往下繼續跑下一個 iter.next()
否則就會如 [3] 那樣跑出 ItemSkippedException。
另外在查資料時也有看到 StackOverflow 上有網友討論說,同樣的問題也有 JEE 的實作方法,因此如果有 JEE 的時候,就不需要額外放 Apache 的函式庫了。
不過一時找不到那篇在哪裡了,改天找到再補上來.....。
參考資料:
1、Jersey multipart streaming without disk buffering
2、Apache Commons Streaming API
3、Why ItemSkippedException?
沒有留言:
張貼留言