【问题标题】:Write an InputStream to an HttpServletResponse将 InputStream 写入 HttpServletResponse
【发布时间】:2012-04-25 21:51:34
【问题描述】:

我有一个要写入 HttpServletResponse 的 InputStream。 有这种方法,由于使用了byte[]

InputStream is = getInputStream();
int contentLength = getContentLength();

byte[] data = new byte[contentLength];
is.read(data);

//response here is the HttpServletResponse object
response.setContentLength(contentLength);
response.write(data);

我想知道就速度和效率而言,最好的方法是什么。

【问题讨论】:

    标签: java jakarta-ee servlets inputstream outputstream


    【解决方案1】:

    只需在块中写入,而不是先将其完全复制到 Java 的内存中。下面的基本示例将其写入 10KB 的块中。这样,您最终得到的内存使用量只有 10KB,而不是完整的内容长度。此外,最终用户将更快地开始获取部分内容。

    response.setContentLength(getContentLength());
    byte[] buffer = new byte[10240];
    
    try (
        InputStream input = getInputStream();
        OutputStream output = response.getOutputStream();
    ) {
        for (int length = 0; (length = input.read(buffer)) > 0;) {
            output.write(buffer, 0, length);
        }
    }
    

    就性能而言,你可以使用 NIO Channels 和直接分配的 ByteBuffer。在一些自定义实用程序类中创建以下实用程序/帮助方法,例如Utils:

    public static long stream(InputStream input, OutputStream output) throws IOException {
        try (
            ReadableByteChannel inputChannel = Channels.newChannel(input);
            WritableByteChannel outputChannel = Channels.newChannel(output);
        ) {
            ByteBuffer buffer = ByteBuffer.allocateDirect(10240);
            long size = 0;
    
            while (inputChannel.read(buffer) != -1) {
                buffer.flip();
                size += outputChannel.write(buffer);
                buffer.clear();
            }
    
            return size;
        }
    }
    

    然后你使用如下:

    response.setContentLength(getContentLength());
    Utils.stream(getInputStream(), response.getOutputStream());
    

    【讨论】:

    • 当然很多工具包已经定义了这个方法,所以一旦你开始使用 Guava...docs.guava-libraries.googlecode.com/git/javadoc/com/google/…, java.io.OutputStream)
    • +1 @BalusC 我们需要设置它的 ContentType 吗?如果有,会是什么?
    • @Roylee:是的,这是推荐的。只需将内容类型设置为内容的类型即可:)freeformatter.com/mime-types-list.html
    • 我试过了,为什么我的流会返回这样的错误:org.apache.catalina.connector.ClientAbortException: java.io.IOException: An established connection was aborted by the software in你的主机在 org.apache.catalina.connector.OutputBuffer.appendByteArray(OutputBuffer.java:778) 在 org.apache.catalina.connector 的 org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:356)。 OutputBuffer.append(OutputBuffer.java:707) 在 org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:391)
    • 如何获取输入Stream的ContentLenght()?
    【解决方案2】:
    BufferedInputStream in = null;
    BufferedOutputStream out = null;
    OutputStream os;
    os = new BufferedOutputStream(response.getOutputStream());
    in = new BufferedInputStream(new FileInputStream(file));
    out = new BufferedOutputStream(os);
    byte[] buffer = new byte[1024 * 8];
    int j = -1;
    while ((j = in.read(buffer)) != -1) {
        out.write(buffer, 0, j);
    }
    

    【讨论】:

    • 如果可能的话,我想避免使用 byte[]
    • @MuhammadSabry 无法使用 byte[] 读取 InputStream
    • byte[] 有什么问题。您需要以某种方式存储数据:}
    【解决方案3】:

    我认为这非常接近最佳方式,但我建议进行以下更改。使用固定大小的缓冲区(比如 20K),然后在循环中进行读/写。

    为循环做类似的事情

    byte[] buffer=new byte[20*1024];
    outputStream=response.getOutputStream();
    while(true) {
      int readSize=is.read(buffer);
      if(readSize==-1)
        break;
      outputStream.write(buffer,0,readSize);
    }
    

    ps:你的程序不会一直按原样运行,因为 read 并不总是填满你给它的整个数组。

    【讨论】:

    • 你到底是什么意思 read 没有填满你给它的整个数组?
    • 读取并不总是填满输入数组。因此,您需要检查读取返回的值,即读取的字节数。 (见docs.oracle.com/javase/6/docs/api/java/io/…
    猜你喜欢
    • 1970-01-01
    • 2016-12-22
    • 2014-06-11
    • 2016-03-19
    • 1970-01-01
    • 1970-01-01
    • 2014-06-03
    • 2013-05-11
    • 1970-01-01
    相关资源
    最近更新 更多