【问题标题】:Sending Large files as stream to process.getOutputStream将大文件作为流发送到 process.getOutputStream
【发布时间】:2010-11-13 05:59:24
【问题描述】:

我在 Windows 机器中使用 gzip 实用程序。我压缩了一个文件并作为 blob 存储在数据库中。当我想使用 gzip 实用程序解压缩此文件时,我正在将此字节流写入 process.getOutputStream。但是在 30KB 之后,它无法读取文件。它挂在那里。

尝试了内存参数、读取和刷新逻辑。但是如果我尝试写入文件,同样的数据会非常快。

 OutputStream stdin = proc.getOutputStream();
 Blob blob = Hibernate.createBlob(inputFileReader);
 InputStream source = blob.getBinaryStream();
 byte[] buffer = new byte[256];
 long readBufferCount = 0;
 while (source.read(buffer) > 0)
 {
  stdin.write(buffer);
  stdin.flush();
  log.info("Reading the file - Read bytes: " + readBufferCount);
  readBufferCount = readBufferCount + 256;
 }
 stdin.flush();

问候, Mani Kumar Adari。

【问题讨论】:

    标签: java runtime io stream outputstream


    【解决方案1】:

    我怀疑问题是外部进程(连接到proc)要么是

    • 未读取其标准输入,或
    • 它正在向其标准输出写入您的 Java 应用程序未读取的内容。

    请记住,Java 使用一对“管道”与外部进程通信,并且这些管道的缓冲量有限。如果超过管道的缓冲容量,写入进程将被阻止写入管道,直到读取进程从管道读取足够的数据以腾出空间。如果读取器不读取,则管道锁定。

    如果您提供更多上下文(例如启动 gzip 进程的应用程序部分),我将能够更加明确。

    跟进

    gzip.exe 是我们正在使用的 Windows 中的一个 unix 实用程序。命令提示符中的 gzip.exe 工作正常。但不适用于java程序。有什么方法可以增加 java 写入管道的缓冲大小。我目前关心的是输入部分。

    在 UNIX 上,gzip 实用程序通常使用以下两种方式之一:

    • gzip file 压缩 file 将其转换为 file.gz
    • ... | gzip | ...(或类似的东西)将其标准输入的压缩版本写入其标准输出。

    我怀疑您正在执行与后者相同的操作,Java 应用程序既是 gzip 命令输入的源又是其输出的目标。这正是可以锁定的场景......如果java应用程序没有正确实现。例如:

        Process proc = Runtime.exec(...);  // gzip.exe pathname.
        OutputStream out = proc.getOutputStream();
        while (...) {
            out.write(...);
        }
        out.flush();
        InputStream in = proc.getInputStream();
        while (...) {
            in.read(...);
        }
    

    如果上面应用的写阶段写入过多数据,保证会锁死。

    java 应用程序和gzip 之间的通信是通过两个管道进行的。正如我上面所说,管道会缓冲一定数量的数据,但这个数量相对较小,而且肯定是有界的。这就是锁定的原因。这是发生了什么:

    1. gzip 进程是用一对将其连接到 Java 应用程序进程的管道创建的。
    2. Java 应用程序将数据写入其out
    3. gzip 进程从其标准输入读取数据,对其进行压缩并写入其标准输出。
    4. 步骤 2. 和 3. 重复几次,直到最后 gzip 进程尝试写入其标准输出块。

    正在发生的事情是gzip 一直在写入其输出管道,但没有任何内容从中读取。最终,我们已经耗尽了输出管道的缓冲区容量,并且写入管道阻塞了。

    与此同时,Java 应用程序仍在写入 out 流,经过几轮之后,这也阻塞了,因为我们已经填满了另一个管道。

    唯一的解决方案是让 Java 应用程序同时读取和写入。执行此操作的简单方法是创建第二个线程并从一个线程写入外部进程并从另一个线程读取进程。

    (更改 Java 缓冲或 Java 读/写大小无济于事。重要的缓冲存在于管道的操作系统实现中,如果有的话,也无法从纯 Java 更改它。)

    【讨论】:

    • 谢谢斯蒂芬。 gzip.exe 是我们正在使用的 Windows 中的一个 unix 实用程序。命令提示符中的 gzip.exe 工作正常。但不适用于java程序。有什么方法可以增加 java 写入管道的缓冲大小。我目前关心的是输入部分。
    • @Mani Kumar Adari 增加缓冲区,即使可行,也无济于事。你应该有一些东西可以读取 gzip.exe 的输出。或者,只使用标准的 GZIPInputStream 类,你根本不需要 gzip.exe。
    猜你喜欢
    • 2013-01-30
    • 2022-01-16
    • 1970-01-01
    • 2017-08-24
    • 2019-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多