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

2023-12-05

我有一个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() {
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当您无法控制第二次读取 ServletInputStream 的代码时,如何多次读取 ServletInputStream 的相关文章

随机推荐

  • 如何用SAX正确解析XML?

    我正在从 REST 服务接收 XML 文档 该文档应使用 SAX 进行解析 请参阅以下由 XSD 生成的示例 设置解析器不是问题 我的主要问题是实际处理startElement endElement 我不明白如何提取我需要的项目并存储它们
  • 套接字:有时(很少)数据包在接收过程中丢失

    我在用着Socket从 udp 多播接收数据 代码很简单 s new Socket AddressFamily InterNetwork SocketType Dgram ProtocolType Udp while true int co
  • 使用 Firebase Simple Login 保护路由

    我正在尝试在使用 Firebase Simple Login 的 Ember 应用程序中实现以下事件序列ember cli 在允许进入任何路由之前检查用户是否经过身份验证 All路由需要经过身份验证 如果用户未通过身份验证 则重定向到Log
  • 使用 Twitter API 版本 1.1 检索 user_timeline 的最简单 Java 示例

    我正在寻找一个使用 Twitter 1 1 API 的简单 Java 示例 但没有找到 使用此处发布的 PHP 示例 使用 Twitter API 版本 1 1 检索 user timeline 的最简单 PHP 示例和其他一些 Stack
  • 根据一列的值合并数据

    我在 R 中有一个数据框 year group sales 1 2000 1 20 2 2001 1 25 3 2002 1 23 4 2003 1 30 5 2001 2 50 6 2002 2 55 我想按组对数据进行分组或创建某种对象
  • 如何使用 html5 重置视频

    我对网络编程有点陌生 我正在尝试弄清楚如何在第一次播放视频后重置视频 我的代码是 var video document getElementById home video video addEventListener click funct
  • 使用 mp3 元数据或 HTML 在 iPhone 锁定屏幕上显示插图和曲目名称

    当您在使用 safari chrome 等后锁定 iPhone 时 当播放 mp3 文件时 它会显示空白的插图和指向 mp3 文件位置的硬链接 看起来相当难看 我可以包含一些替代标签 元数据来显示艺术作品和曲目标题吗 您可以通过设置添加一个
  • 我应该如何处理 APP_KEY 和 APP_SECRET (Dropbox API)

    我使用 Dropbox API 编写了一段简单的代码 这意味着使用我的应用程序的 APP KEY 和 APP SECRET 假设有人也想使用我的应用程序 我创建了一个 github 存储库 推送代码等等 但是 当然 我不放置 APP KEY
  • 将数据从固定长度文件读取到类对象中

    我有一个固定长度的文件 想将其数据读入类对象中 这些对象将进一步用于在数据库中插入 更新数据 虽然可以使用 StreamReader 来完成 但我正在寻找更复杂的解决方案 FileHelper 是另一种解决方案 但我不想在我的程序中使用开源
  • Delphi中'Result'的默认值是多少?

    是否有任何保证的默认值Result函数的变量 如 0 或 nil 或者应该Result使用前总是要初始化吗 我有一个函数返回这样的字符串 function Foo String begin while do Result Result bo
  • 有没有一种简单的方法将MySQL数据转换为标题大小写?

    我有一个 MySQL 表 其中一列中的所有数据均以大写形式输入 但我需要转换为标题大小写 并识别类似于大胆的火球标题案例脚本 I found 这个优秀的解决方案用于将字符串转换为小写 但 Title Case 函数似乎已被排除在我的 MyS
  • 从另一个 ViewController 调用函数

    我有两个 ViewController FirstViewController and SecondViewController 两者都有自己的 Swift 文件 FirstViewController swift and SecondVi
  • android 中的自定义进度对话框?

    我按照以下步骤操作自定义对话框示例在文档中 但我得到了这个例外 有任何想法吗 04 03 18 50 28 787 VERBOSE Bru Press Tab 750 Exception in Tabsjava lang RuntimeEx
  • FSI.exe 在 Ubuntu 10.10 下不起作用

    更新 尝试过 11 月 CTP 版本 同样的错误消息 忘了说这个服务器安装了 Ubuntu 服务器版本 我在运行 Desktop 10 10 的 Ubuntu 桌面上没有遇到此问题 所以我怀疑可能是缺少一些命令行相关的库 安装 librea
  • 动态实例属性

    假设我有一堂课 class Foo object def init self d self d d d a 1 b 2 inst Foo d inst d Out 315 a 1 b 2 有没有办法动态创建 n 个属性 其中每个属性都是一个
  • jQuery 获取 select onChange 的值

    我的印象是我可以通过这样做来获取选择输入的值 this val 并应用onchange参数到选择字段 看起来只有当我引用 ID 时它才有效 我该如何使用这个来做到这一点 尝试这个 select on change function aler
  • 将无聊的事情自动化 - 第 4 章:硬币翻转解决方案

    我正在为 python 编程的 自动化无聊的事情 第 4 章末尾的 硬币翻转 实践项目的解决方案而苦苦挣扎 我有两个解决方案 两者都产生完全不同的结果 第一个显然是错误的 我不确定答案的正确解决方案是什么 解决方案一 import rand
  • C++ 结构成员模板函数的显式专业化 - 这是 Visual Studio 问题吗?

    我对模板专业化有疑问 可以归结为以下代码片段 include
  • IIS 10 应用程序池回收后初始化速度缓慢

    我们的应用程序池在 IIS 10 中的应用程序池回收后初始化时间较慢 回收后大约 5 7 秒 第一个请求后 30 50 毫秒 我做了一些研究 发现 应用程序初始化 模块应该可以解决问题 我将其安装到服务器上 并将应用程序池设置为 Alway
  • 当您无法控制第二次读取 ServletInputStream 的代码时,如何多次读取 ServletInputStream

    我有一个ServletInputStream我需要多次读取 第二次读取的代码位于我无法控制的 API 中 使用时IOUtils copy 看起来仍然只允许读取一次流 流上不允许标记 重置 有任何想法吗 谢谢 创建一个扩展 HttpServl