Java 如何在不';我无法控制第二次读取它的代码

Java 如何在不';我无法控制第二次读取它的代码,java,io,Java,Io,我有一个ServletInputStream,我需要多次读取它(第二次读取它的代码在我不控制的API中)。使用IOUtils.copy时,流似乎仍然只允许读取一次(流上不允许标记/重置) 有什么想法吗?谢谢。创建一个扩展HttpServletRequestWrapper的类。此类将原始请求的输入流中的内容缓存在临时文件中 public class CachedHttpServletRequest extends HttpServletRequestWrapper { public st

我有一个
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));
   }
}
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"));
    }
}

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

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));
   }
}
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。但是你应该解释为什么你需要它,以及你在更高层次上想做什么,因为可能有不同的解决方案。谢谢。我从HttpServletRequest对象获取流,该对象被用作API方法的输入。看起来他们直接从已经读取的请求对象获取输入流。这个API对该流做了什么?您的代码对流做了什么?我不确定API对它做了什么。我现在正想弄明白。我需要看看请求的JSON有效负载中有什么。那么,你调用了一个API的方法,但你不知道这个方法在做什么?