2015-01-26 61 views
3

我有一個ServletInputStream,我需要多次讀取(第二次讀取它的代碼是我不控制的API)。當使用IOUtils.copy時,它似乎仍然只允許讀取一次(在流上不允許標記/重置)。如何讀取一個ServletInputStream不止一次,當你沒有控制第二次讀取它的代碼

任何想法?謝謝。

+1

在字節數組中讀取它,在此字節數組上創建ByteArrayInputStream,並將此ByteArrayInputStream傳遞給API。但是你應該解釋爲什麼你需要這個以及你想要在更高層次上做什麼,因爲可能有不同的解決方案。 – 2015-01-26 21:13:48

+0

謝謝。我從HttpServletRequest對象獲取流,該對象正在用作API方法的輸入。看起來他們直接從請求對象獲取輸入流,該請求對象在該點已經被讀取。 – mstrom 2015-01-26 21:17:41

+0

此API如何處理流?你使用什麼代碼處理流? – 2015-01-26 21:18:57

回答

3

創建一個擴展HttpServletRequestWrapper的類。該類將緩存原始請求的輸入流中的內容到臨時文件中。

public class CachedHttpServletRequest extends HttpServletRequestWrapper 
{ 

    public static final String TEMPORARY_FILENAME_PREFIX = "MyPrefix"; 
    public static final String TEMPORARY_FILENAME_SUFFIX = ".cache"; 

    public static final int LEN_BUFFER = 32768; //32 KB 


    private File m_TemporaryFile; 


    public CachedHttpServletRequest(HttpServletRequest httpServletRequest, File temporaryFolder) 
     throws ServletException { 

     super(httpServletRequest); 

     try { 
      //Create a temporary file to hold the contents of the request's input stream 
      m_TemporaryFile = File.createTempFile(TEMPORARY_FILENAME_PREFIX, null, temporaryFolder); 

      //Copy the request body to the temporary file 
      BufferedInputStream is = new BufferedInputStream(super.getInputStream()); 
      FileOutputStream os = new FileOutputStream(m_TemporaryFile); 
      byte[] buffer = new byte[LEN_BUFFER]; 
      int bytesWritten = 0; 
      int bytesRead = is.read(buffer); 
      while(bytesRead != -1) { 
       os.write(buffer, 0, bytesRead); 
       bytesWritten += bytesRead; 
       bytesRead = is.read(buffer); 
      } 
      is.close(); 
      os.close(); 
     } 
     catch(Exception e) { 
      throw new ServletException(e); 
     } 
    } 


    public void cleanup() { 
     m_TemporaryFile.delete(); 
    } 


    @Override 
    public ServletInputStream getInputStream() throws IOException { 
     return new CachedServletInputStream(m_TemporaryFile); 
    } 


    @Override 
    public BufferedReader getReader() throws IOException { 
     String enc = getCharacterEncoding(); 
     if(enc == null) enc = "UTF-8"; 
     return new BufferedReader(new InputStreamReader(getInputStream(), enc)); 
    } 
} 

創建一個擴展ServletInputStream的類。調用getInputStream()或getReader()時,您的請求封裝類將返回此自定義輸入流的實例。自定義輸入流類將使用臨時文件打開緩存的內容。

public class CachedServletInputStream extends ServletInputStream { 

    private File m_TemporaryFile; 
    private InputStream m_InputStream; 


    public CachedServletInputStream(File temporaryFile) throws IOException { 
     m_TemporaryFile = temporaryFile; 
     m_InputStream = null; 
    } 


    private InputStream acquireInputStream() throws IOException { 
     if(m_InputStream == null) { 
      m_InputStream = new FileInputStream(m_TemporaryFile); 
     } 

     return m_InputStream; 
    } 


    public void close() throws IOException { 
     try { 
      if(m_InputStream != null) { 
       m_InputStream.close(); 
      } 
     } 
     catch(IOException e) { 
      throw e; 
     } 
     finally { 
      m_InputStream = null; 
     } 
    } 


    public int read() throws IOException { 
     return acquireInputStream().read(); 
    } 


    public boolean markSupported() { 
     return false; 
    } 


    public synchronized void mark(int i) { 
     throw new UnsupportedOperationException("mark not supported"); 
    } 


    public synchronized void reset() throws IOException { 
     throw new IOException(new UnsupportedOperationException("reset not supported")); 
    } 
} 

創建一個實現javax.servlet.Filter的一個類實例化自定義請求包裝,當它檢測到需要緩存的輸入數據流的行爲的請求。

public class CachedHttpServletRequestFilter implements Filter { 

    public static final String HTTP_HEADER_CONTENT_TYPE = "Content-Type"; 
    public static final String MIME_APPLICATION__X_WWW_FORM_URL_ENCODED = "application/x-www-form-urlencoded"; 


    private File m_TemporaryFolder; 


    public CachedHttpServletRequestFilter() { 
     m_TemporaryFolder = new File(/*...your temporary directory goes here...*/); 
    } 


    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 
     throws IOException, ServletException { 

     if(servletRequest instanceof HttpServletRequest) { 
      HttpServletRequest request = (HttpServletRequest) servletRequest; 
      // Check wether the current request needs to be able to support the body to be read multiple times 
      String contentType = StringHelper.getLowercaseTrimmed(request.getHeader(HTTP_HEADER_CONTENT_TYPE)); 

      if(contentType.equals(MIME_APPLICATION__X_WWW_FORM_URL_ENCODED)) { 
       // Override current HttpServletRequest with custom implementation 
       CachedHttpServletRequest cachedRequest = new CachedHttpServletRequest(request, m_TemporaryFolder); 
       filterChain.doFilter(cachedRequest, servletResponse); 
       cachedRequest.cleanup(); 
       return; 
      } 
     } 

     filterChain.doFilter(servletRequest, servletResponse); 
    } 


    public void init(FilterConfig filterConfig) throws ServletException { 

     try { 
      /* ...initialize where your temporary folder is located here... */ 
      //m_TemporaryFolder = new File(/*...*/); 
     } 
     catch(Exception e) { 
      throw new ServletException(e); 
     } 
    } 


    public void destroy() { 
    } 
} 
1

如果要檢查或移交的請求到這樣的API方法之前消耗部分或全部請求主體的,那麼你可能無法做到這一點,在這個Servlet API的用戶級別。相反,您需要降低一點,一方面需要使用Servlet API,另一方面也可以將其提供給其他API。

具體來說,可以保留通過任何手段你選擇從請求的輸入流中讀取數據,並通過在實施包裝HttpServletRequest是大多委託給包裝的請求對象提供相同的其他API,但其getInputStream()方法提供了一個可以讀取整個請求體的流。