当您无法控制第二次读取它的代码时,如何多次读取ServletInputStream [英] How to read a ServletInputStream more than once when you don't have control of the code that reads it a second time

查看:149
本文介绍了当您无法控制第二次读取它的代码时,如何多次读取ServletInputStream的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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.copy, it still seems that the stream is only allowed to be read once (marked/reset is not allowed on the stream).

任何想法?谢谢。

推荐答案

创建一个扩展HttpServletRequestWrapper的类。此类将在原始请求的输入流中缓存临时文件中的内容。

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));
   }
}

创建一个扩展ServletInputStream的类。当调用getInputStream()或getReader()时,您的请求包装器类将返回此自定义输入流的实例。自定义输入流类将使用临时文件打开缓存的内容。

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"));
    }
}

创建一个实现javax.servlet.Filter的类当它检测到需要缓存输入流行为的请求时,实例化您的自定义请求包装。

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() {
    }
}

这篇关于当您无法控制第二次读取它的代码时,如何多次读取ServletInputStream的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆