在業務場景中遇到了需要重用inputStream的情況。伺服器端接收到來自客戶端的request後,從request.getInputStream()獲取的servletInputStream中讀取型別引數,對特定型別,需要將該servletInputStream作為request引數轉發到其它服務程式繼續處理。由於讀取型別引數時,servletInputStream已經被讀取過,其pos已經指向servletInputStream中間的某個位置,當我們需要轉發時,無法獲取到原始的servletInputStream。
// mark方法標記了流的當前位置;// 之後透過reset()方法可以將讀取位置重新定位到標記位置。// readlimit 引數表示此輸入流已經讀取readlimit位元組後,標記位置失效,也就不能reset了。public synchronized void mark(int readlimit) {}public synchronized void reset() throws IOException { throw new IOException("mark/reset not supported"); }// markSupported表示當前流是否支援標記流;InputStream預設不支援。public boolean markSupported() { return false; }
從原始碼可知,ServletInputStream並沒有重寫以上三個方法,也就是其不支援標記流,也就不能重置該流。
快取inputStream不允許對ServletInputStream的多次讀取,那就把它快取起來;這裡透過HttpServletRequestWrapper儲存流的副本。
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper { private byte[] body; public MyHttpServletRequestWrapper(HttpServletRequest request) { super(request); try { body = IOUtils.toByteArray(request.getInputStream()); } catch (IOException ex) { body = new byte[0]; } } @Override public ServletInputStream getInputStream() throws IOException { return new ServletInputStream() { ByteArrayInputStream bais = new ByteArrayInputStream(body); @Override public int read() throws IOException { return bais.read(); } }; }}
總結遇到複用inputStream的情況,首先檢視其原始碼,是否支援mark(markSupported());
如果支援mark,就採用mark和reset方式來複用inputStream;但該方法需要指定一個readlimit值,需要根據業務妥善設定該值,否則會出問題。
對於不支援mark的流,需要將流快取起來;快取必然佔用記憶體,只在必要的時候快取流,並且在用完之後妥善關閉。
你愛的科技內容都在這裡。
最新評論