32

私はファイルのInputStreamを持っており、次のようにapachepoiコンポーネントを使用してファイルから読み取ります。

POIFSFileSystem fileSystem = new POIFSFileSystem(inputStream);

問題は、同じストリームを複数回使用する必要があり、POIFSFileSystemが使用後にストリームを閉じることです。

入力ストリームからデータをキャッシュしてから、さまざまなPOIFSFileSystemにさらに多くの入力ストリームを提供するための最良の方法は何ですか?

編集1:

キャッシュとは、アプリケーションを高速化する方法としてではなく、後で使用するために保存することを意味しました。また、入力ストリームを配列または文字列に読み込んでから、使用するたびに入力ストリームを作成する方がよいでしょうか。

編集2:

質問を再度開いて申し訳ありませんが、デスクトップとWebアプリケーション内で作業する場合の条件は多少異なります。まず、Tomcat Webアプリのorg.apache.commons.fileupload.FileItemから取得したInputStreamはマーキングをサポートしていないため、リセットできません。

次に、ファイルをメモリに保持して、アクセスを高速化し、ファイルを処理する際のioの問題を減らしたいと考えています。

4

10 に答える 10

23

マークおよびリセット機能を別の入力ストリームに追加し、そのcloseメソッドをオーバーライドするBufferedInputStreamを試してください。

public class UnclosableBufferedInputStream extends BufferedInputStream {

    public UnclosableBufferedInputStream(InputStream in) {
        super(in);
        super.mark(Integer.MAX_VALUE);
    }

    @Override
    public void close() throws IOException {
        super.reset();
    }
}

それで:

UnclosableBufferedInputStream  bis = new UnclosableBufferedInputStream (inputStream);

bis以前にinputStreamが使用されていた場所で使用します。

于 2009-08-20T00:15:33.557 に答える
23

close() が呼び出されたときに reset() で応答するバージョンでPOIFSFileSystemに渡される InputStream をデコレートできます。

class ResetOnCloseInputStream extends InputStream {

    private final InputStream decorated;

    public ResetOnCloseInputStream(InputStream anInputStream) {
        if (!anInputStream.markSupported()) {
            throw new IllegalArgumentException("marking not supported");
        }

        anInputStream.mark( 1 << 24); // magic constant: BEWARE
        decorated = anInputStream;
    }

    @Override
    public void close() throws IOException {
        decorated.reset();
    }

    @Override
    public int read() throws IOException {
        return decorated.read();
    }
}

テストケース

static void closeAfterInputStreamIsConsumed(InputStream is)
        throws IOException {
    int r;

    while ((r = is.read()) != -1) {
        System.out.println(r);
    }

    is.close();
    System.out.println("=========");

}

public static void main(String[] args) throws IOException {
    InputStream is = new ByteArrayInputStream("sample".getBytes());
    ResetOnCloseInputStream decoratedIs = new ResetOnCloseInputStream(is);
    closeAfterInputStreamIsConsumed(decoratedIs);
    closeAfterInputStreamIsConsumed(decoratedIs);
    closeAfterInputStreamIsConsumed(is);
}

編集2

byte[] (slurp モード) でファイル全体を読み取り、それを ByteArrayInputStream に渡すことができます

于 2009-05-29T08:46:44.783 に答える
5

これは正しく動作します:

byte[] bytes = getBytes(inputStream);
POIFSFileSystem fileSystem = new POIFSFileSystem(new ByteArrayInputStream(bytes));

getBytes は次のようになります。

private static byte[] getBytes(InputStream is) throws IOException {
    byte[] buffer = new byte[8192];
ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
int n;
baos.reset();

while ((n = is.read(buffer, 0, buffer.length)) != -1) {
      baos.write(buffer, 0, n);
    }

   return baos.toByteArray();
 }
于 2011-02-15T15:20:32.680 に答える
1

ファイルがそれほど大きくない場合は、配列に読み込み、その配列から作成されたbyte[]POIを指定します。ByteArrayInputStream

ファイルが大きい場合は、OS が可能な限りキャッシュを行うため、気にする必要はありません。

[編集]効率的な方法でファイルをバイト配列に読み込むには、Apache commons-ioを使用します。int read()ファイルをバイトごとに読み取るため、非常に遅いので使用しないでください。

自分でやりたい場合は、Fileオブジェクトを使用して長さを取得し、配列と、ファイルからバイトを読み取るループを作成します。バイトread(byte[], int offset, int len)未満を読み取ることができるため、ループする必要があります(通常はそうします)。len

于 2009-05-29T08:43:25.023 に答える
1

これは、任意の InputStream で安全に使用するために、私が実装する方法です:

  • 元のストリーム コンテンツをミラーリングする一時ファイルを作成する独自の InputStream ラッパーを作成する
  • 元の入力ストリームから読み取ったすべてをこの一時ファイルにダンプします
  • ストリームが完全に読み取られると、すべてのデータが一時ファイルにミラーリングされます
  • InputStream.reset を使用して、内部ストリームを FileInputStream(mirrored_content_file) に切り替える (初期化する)
  • これからは、元のストリームの参照を失います(収集できます)
  • 一時ファイルを削除し、開いているストリームを解放する新しいメソッド release() を追加します。
  • release()を呼び出すのを忘れた場合に備えて、finalizeからrelease() を呼び出して、一時ファイルが解放されていることを確認することもできます (ほとんどの場合、 finalizeの使用を避ける必要があり、常にオブジェクト リソースを解放するメソッドを呼び出します)。finalize() を実装する理由を参照してください。
于 2009-05-29T14:12:29.397 に答える
1

「キャッシュ」とは正確には何を意味しますか?ストリームの先頭から別の POIFSFileSystem を開始しますか? もしそうなら、あなたの Java コードに何かをキャッシュしても意味がありません。これは OS によって行われます。新しいストリームを開くだけです。

それとも、最初の POIFSFileSystem が停止した時点から読み続けますか? それはキャッシングではなく、実行するのは非常に困難です。ストリームが閉じられるのを避けられない場合に考えられる唯一の方法は、読み取られたバイト数をカウントする薄いラッパーを作成し、新しいストリームを開いてそのバイト数をスキップすることです。しかし、POIFSFileSystem が BufferedInputStream のようなものを内部的に使用している場合、これは失敗する可能性があります。

于 2009-05-29T08:46:21.897 に答える
1
public static void main(String[] args) throws IOException {
    BufferedInputStream inputStream = new BufferedInputStream(IOUtils.toInputStream("Foobar"));
    inputStream.mark(Integer.MAX_VALUE);
    System.out.println(IOUtils.toString(inputStream));
    inputStream.reset();
    System.out.println(IOUtils.toString(inputStream));
}

これは機能します。IOUtils は commons IO の一部です。

于 2010-12-18T01:08:30.350 に答える