我有一個ServletInputStream
,我需要多次讀取(第二次讀取它的代碼是我不控制的API)。當使用IOUtils.copy
時,它似乎仍然只允許讀取一次(在流上不允許標記/重置)。如何讀取一個ServletInputStream不止一次,當你沒有控制第二次讀取它的代碼
任何想法?謝謝。
我有一個ServletInputStream
,我需要多次讀取(第二次讀取它的代碼是我不控制的API)。當使用IOUtils.copy
時,它似乎仍然只允許讀取一次(在流上不允許標記/重置)。如何讀取一個ServletInputStream不止一次,當你沒有控制第二次讀取它的代碼
任何想法?謝謝。
創建一個擴展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() {
}
}
如果要檢查或移交的請求到這樣的API方法之前消耗部分或全部請求主體的,那麼你可能無法做到這一點,在這個Servlet API的用戶級別。相反,您需要降低一點,一方面需要使用Servlet API,另一方面也可以將其提供給其他API。
具體來說,可以保留通過任何手段你選擇從請求的輸入流中讀取數據,並通過在實施包裝的HttpServletRequest
是大多委託給包裝的請求對象提供相同的其他API,但其getInputStream()
方法提供了一個可以讀取整個請求體的流。
在字節數組中讀取它,在此字節數組上創建ByteArrayInputStream,並將此ByteArrayInputStream傳遞給API。但是你應該解釋爲什麼你需要這個以及你想要在更高層次上做什麼,因爲可能有不同的解決方案。 – 2015-01-26 21:13:48
謝謝。我從HttpServletRequest對象獲取流,該對象正在用作API方法的輸入。看起來他們直接從請求對象獲取輸入流,該請求對象在該點已經被讀取。 – mstrom 2015-01-26 21:17:41
此API如何處理流?你使用什麼代碼處理流? – 2015-01-26 21:18:57