I have a ServletInputStream
that I need to read more than once (the code that reads it the second time is in an API I don\'t control). When using IOUtils.
Create a class that extends HttpServletRequestWrapper. This class will cache the contents on the original request's input stream in a temporary file.
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));
}
}
Create a class that extends ServletInputStream. Your request wrapper class will return an instance of this custom input stream when getInputStream() or getReader() is called. The custom input stream class will open the cached contents using the temporary file.
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"));
}
}
Create a class that implements javax.servlet.Filter that instantiates your custom request wrapper when it detects a request that requires cached input stream behavior.
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() {
}
}