java - 当您无法控制第二次读取 ServletInputStream 的代码时,如何多次读取它

标签 java io

我有一个 ServletInputStream 需要多次读取(第二次读取它的代码在我无法控制的 API 中)。使用IOUtils.copy时,似乎仍然只允许读取一次流(流上不允许标记/重置)。

有什么想法吗?谢谢。

最佳答案

创建一个扩展 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() {
    }
}

关于java - 当您无法控制第二次读取 ServletInputStream 的代码时,如何多次读取它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28158826/

相关文章:

java - 我可以关闭由不属于我的代码打开的文件句柄吗?

java - 无法使用 float 按钮 AlertDialog 在 SQLite 数据库中插入数据

java - Hibernate 和 spring mvc 删除和更新

java - 它们是完全相同的方法,但一种有效,另一种无效

c - 在C中的int数组中查找数字的频率

java - 测量 GPRS 和 3G 带宽的正确方法

java - 为后台进程编写测试(如后台作业)

java - 我想将 java 中的 .txt 文件中的多个字符串解析为变量。最简单的方法是什么?

java - jsp页面布局的最佳实践是什么?

java - 重复附加到字符串时无法添加换行符