【问题标题】:Get an input stream from an output stream从输出流中获取输入流
【发布时间】:2015-05-30 22:47:43
【问题描述】:

我有一个组件在输出流 (ByteArrayOutputStream) 中为我提供数据,我需要将其写入 SQL 数据库的 blob 字段而不创建临时缓冲区,因此需要获取输入流。

根据herehere 的回答,我想出了以下方法从输出流中获取输入流:

private PipedInputStream getInputStream(ByteArrayOutputStream outputStream) throws InterruptedException
{
    PipedInputStream pipedInStream = new PipedInputStream();
    Thread copyThread = new Thread(new CopyStreamHelper(outputStream, pipedInStream));
    copyThread.start();
    // Wait for copy to complete
    copyThread.join();
    return pipedInStream;
}

class CopyStreamHelper implements Runnable
{
    private ByteArrayOutputStream outStream;
    private PipedInputStream pipedInStream;

    public CopyStreamHelper (ByteArrayOutputStream _outStream, PipedInputStream _pipedInStream)
    {
        outStream = _outStream;
        pipedInStream = _pipedInStream;
    }

    public void run()
    {
        PipedOutputStream pipedOutStream = null;
        try
        {
            // write the original OutputStream to the PipedOutputStream
            pipedOutStream = new PipedOutputStream(pipedInStream);
            outStream.writeTo(pipedOutStream);
        }
        catch (IOException e) 
        {
            // logging and exception handling should go here
        }
        finally
        {
            IOUtils.closeQuietly(pipedOutStream);
        }
    }
}

请注意,输出流已经包含写入的数据,它最多可以运行 1-2 MB。 然而,无论尝试在两个单独的线程或同一个线程中执行此操作,我发现总是PipedInputStream 挂在以下位置:

Object.wait(long) line: not available [native method]   
PipedInputStream.awaitSpace() line: not available   

【问题讨论】:

  • 组件应该返回一个 InputStream。 OutputStream 是一个接收器,并不意味着被读取。在这种情况下,这是可能的,因为 ByteArrayOutputStream 的特殊性质。您避免临时缓冲区的目标会丢失,因为 ByteArrayOutputStream 在传递时已经包含完整数据。
  • @Sponiro,同意,但我想防止创建另一个缓冲区只是为了获取输入流。所述组件生成输出,所以我不清楚它如何返回 InputStream?
  • 一个简单的解决方案是先将数据写入文件,然后打开 InputStream 进行读取。如果您真的想将 PipedInputStream 用于真正的生产者-消费者场景,则需要使用两个线程。这是完全可能的,但有点复杂。您的组件将存在于一个线程中并分发一个 InputStream(实际上是一个 PipedInputStream),另一个线程将选择那个并从中读取。您上面的解决方案创建了一个线程并等待其结果,这是非常不同的。
  • @Sponiro,我考虑并放弃了写入文件,因为我认为这是不必要的。我在这里所拥有的不是通常意义上的生产者-消费者场景,两者都可以异步发生。这里的情况是组件已完成输出并将数据写入 OutputStream。现在我需要获取该数据并通过 SQL 语句将其发送到数据库。鉴于此,我试图找到最有效的方法。从看起来这可能是不可能的 - 这意味着链接的 SO 问题上的其他类似答案不起作用?
  • @Sponiro,继续...关于需要两个线程才能使管道流正常工作,我上面显示的代码 正在 这样做 - 你看到了什么错了,因为它没有按我的预期工作。

标签: java inputstream outputstream


【解决方案1】:

您的解决方案过于复杂

ByteArrayOutputStream baos = ...;
byte[] data = baos.toByteArray();
return new ByteArrayInputStream(data);

【讨论】:

  • OP 说他想做without creating temp buffers
  • 我试图避免创建临时缓冲区,因为数据大小非常大(1-2 MB),并且会有许多并行线程在执行此操作。如果我无法解决原始问题,这是我的后备解决方案。
【解决方案2】:

在某种程度上必须有一个缓冲区。见Connecting an input stream to an outputstream

我最喜欢的答案来自 Dean Hiller:

void feedInputToOutput(InputStream in, OutputStream out) {
   IOUtils.copy(in, out);
}

详情请见the api

【讨论】:

  • 我需要的恰恰相反,得到一个 InputStream 以便我可以从 OutputStream 中读取。
  • 输入流没有输出流。输入流是源;您从输入流中读取内容。一个输出流是一个接收器;您将内容写入输出流。如果您正在寻找可以读取和写入的内容,也许您的列表或队列会更合适。
  • Andreas - 我相信 Santosh 正试图将输出流的内容重定向到另一个输入流。我也有同样的问题。我得到一个缩略图作为输出流,我希望通过输入流通过 HTTP 提供它。
【解决方案3】:

我制作了一个非常简单的使用 PipedInput/OutputStream 的演示。它可能适合也可能不适合您的用例。

写入 PipedOutputStream 的生产类:

public class Producer implements Runnable {

    private final PipedOutputStream pipedOutputStream;
    private final PipedInputStream pipedInputStream;

    public Producer() throws IOException {
        this.pipedOutputStream = new PipedOutputStream();
        this.pipedInputStream = new PipedInputStream(pipedOutputStream);
    }

    public PipedInputStream getPipedInputStream() {
        return pipedInputStream;
    }

    @Override
    public void run() {

        try(InputStream inputStream = ByteStreams.limit(new RandomInputStream(), 100000)) {
            // guava copy function
            ByteStreams.copy(inputStream, pipedOutputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                pipedOutputStream.close();
            } catch (IOException e) {
                // no-op
            }
        }
    }

    public static void main(String[] args) throws IOException {

        try {
            Producer producer = new Producer();
            Consumer consumer = new Consumer(producer);

            Thread thread1 = new Thread(producer);
            Thread thread2 = new Thread(consumer);

            thread1.start();
            thread2.start();

            thread1.join();
            thread2.join();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

只计算字节数的消费者:

public class Consumer implements Runnable {

    private final Producer producer;

    public Consumer(Producer producer) {
        this.producer = producer;
    }

    @Override
    public void run() {

        try (PipedInputStream pipedInputStream = producer.getPipedInputStream()) {

            int counter = 0;
            while (pipedInputStream.read() != -1) {
                counter++;
            }

            System.out.println(counter);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-25
    • 2014-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多