0

古き良きInputStreamから読んでいる間、私は次のコードを使用しました(私は決して快適ではありませんでした):

    int read = 0;
    InputStream is = ....;

    while((i = is.read() != -1){

        ....
    }

今私はNIOを使用してInputStreamから10MBを読み取ろうとしています:

        protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub

        System.out.println("In Controller.doPost(...)");

        ByteBuffer chunk = ByteBuffer.allocateDirect(1000000);

        /* Source channel */
        int numRead = 0;
        ReadableByteChannel rbc = Channels.newChannel(request.getInputStream());

        /* Destination channel */
        File destFile = new File(
                "D:\\SegyDest.sgy");
        FileOutputStream destFileFos = new FileOutputStream(destFile);
        FileChannel destFileChannel = destFileFos.getChannel();

        /* Read-Write code */
        while (numRead >= 0) {
            chunk.rewind();
            numRead = rbc.read(chunk);

            System.out.println("numRead = " + numRead);

            chunk.rewind();
            destFileChannel.write(chunk);
        }

        /* clean-up */
        rbc.close();
        destFileChannel.close();
        destFileFos.close();

        request.setAttribute("ops", "File Upload");
        request.getRequestDispatcher("/jsp/Result.jsp").forward(request,
                response);
    }

私の質問は/*ソースチャネルをループしてすべてのバイトを読み取る方法は?* /

4

2 に答える 2

1

または、次のようにAPIを1バイトを超えるチャンクでIOを実行します。

byte[] bA = new byte[4096];
int i;
InputStream is = ....;
OutputStream os = ....;

while((i = is.read(bA) != -1){
    os.write(bA, 0, i);
}

私はあなたの他の質問を見ました、そして私のコメントはまだ立っています。NIOはあなたが探しているソリューションではありません。プロキシとして機能するRAMが制限されたローエンドマシンがあります。

最善の方法は、サーブレットに新しいスレッドを作成させ、このスレッドにNIOソケット/HTTPライブラリを使用して発信接続を作成および設定させることです。この新しい(そして追加の)スレッドは、3つの事柄のいずれかが発生するのを待っており、APIをプッシュして、これら3つの領域で進歩を遂げようとします。

3つのことは次のとおりです。

  • リモートサーバーにデータを書き込もうとしています(送信するデータがメモリにバッファリングされている場合)
  • メインサーブレットスレッドが共有バッファに新しいデータがあることを示すのを待っています。または、そのストリームの終わりに到達しました。
  • メインのサーブレットスレッドが追加のスレッドをシャットダウンする必要があることを示すのを待っています(これはエラーの回復とクリーンアップです)。

おそらく、doPost()メソッドが追加のスレッドを呼び出して最終データをリモートサーバーにプッシュする時間を与えるためのdrainWithTimeout(long millis)関数が必要です。これは、サーブレットがInputStreamから監視した場合、End-of-Streamのときに呼び出されます。

doPost()メソッドが戻る前に、余分なスレッドが100%確実に刈り取られることを確認する必要があります。したがって、特に送信側クライアントが切断されたか、アイドル状態が長すぎるためにInputStreamでエラーが発生したシナリオでは、その起動/シャットダウンを制御することが重要です。

次に、2つのスレッド(doPost()の通常のサーブレットスレッドと作成した新しいスレッド)が任意のメモリバッファー(共有される16Mb以上)をセットアップして共有します。

クライアント/コンカレントユーザーと2GbRAMの制限のために16Mbバッファーを使用できない場合は、ネットワークとO / Sカーネルがすでに一部のMbをバッファーしているため、この回答の上部にあるサンプルコードを使用する必要があります。データの。

2つのスレッドを使用するポイントは、データを受信するサーブレットAPIがブロッキングI / O APIであるという問題を修正できないことです。サーブレットの仕様/標準に準拠するようにアプリケーションを作成している場合、それを変更することはできません。特定のサーブレットコンテナに機能があることがわかっている場合、それはこの回答の範囲外です。

2つのスレッドにより、メインのサーブレットdoPostスレッドを制御し、InputStreamにブロッキングI /OAPIを使用できます。

1つのスレッドとブロックするInputStreamを非ブロックのOutputStreamで使用しても意味がありません。それでも、in.read()API呼び出しがブロックされている間(追加のデータまたはEnd-を待機している間)は出力ストリームを処理できないという問題があります。 of-stream)。

于 2012-09-20T09:51:30.407 に答える
0

NIOチャネル間でコピーする正しい方法は次のとおりです。

while (in.read(buffer) > 0 || buffer.position() > 0)
{
  buffer.flip();
  out.write(buffer);
  buffer.compact();
}

これにより、EOS、部分読み取り、および部分書き込みが自動的に処理されることに注意してください。

于 2012-09-24T10:59:58.617 に答える