あるストリームのコンテンツを別のストリームにコピーするにはどうすればよいですか?
質問
あるストリームのコンテンツを別のストリームにコピーする最良の方法は何ですか?このための標準的なユーティリティメソッドはありますか?
解決
.NET 4.5以降では、 Stream.CopyToAsync
メソッド
input.CopyToAsync(output);
これは、 Task を返します。 code>
は、完了時に続行できます。
await input.CopyToAsync(output)
// Code from here on will be run in a continuation.
CopyToAsync
の呼び出しが行われた場所に応じて、それに続くコードは、それを呼び出した同じスレッドで継続する場合としない場合があります。
SynchronizationContext
await
継続が実行されるスレッドを決定します。
さらに、この呼び出し(および、これは変更される可能性のある実装の詳細です)は、読み取りと書き込みをシーケンスします(I / O完了時にブロックするスレッドを無駄にしません)。
.NET 4.0以降には、 Stream.CopyTo
メソッド
input.CopyTo(output);
.NET 3.5以前の場合
これを支援するためにフレームワークに焼き付けられたものはありません。次のように、コンテンツを手動でコピーする必要があります。
public static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[32768];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write (buffer, 0, read);
}
}
注1:このメソッドを使用すると、進行状況をレポートできます(これまでに読み取られたxバイト...)
注2:なぜ input.Length
ではなく固定バッファサイズを使用するのですか?その長さは利用できない可能性があるためです! ドキュメントから:
Streamから派生したクラスがシークをサポートしていない場合、Length、SetLength、Position、およびSeekの呼び出しはNotSupportedExceptionをスローします。
他のヒント
MemoryStreamには.WriteTo(outstream);
がありますおよび.NET 4.0には、通常のストリームオブジェクトに.CopyToがあります。
.NET 4.0:
instream.CopyTo(outstream);
次の拡張メソッドを使用します。 1つのストリームがMemoryStreamである場合のオーバーロードが最適化されています。
public static void CopyTo(this Stream src, Stream dest)
{
int size = (src.CanSeek) ? Math.Min((int)(src.Length - src.Position), 0x2000) : 0x2000;
byte[] buffer = new byte[size];
int n;
do
{
n = src.Read(buffer, 0, buffer.Length);
dest.Write(buffer, 0, n);
} while (n != 0);
}
public static void CopyTo(this MemoryStream src, Stream dest)
{
dest.Write(src.GetBuffer(), (int)src.Position, (int)(src.Length - src.Position));
}
public static void CopyTo(this Stream src, MemoryStream dest)
{
if (src.CanSeek)
{
int pos = (int)dest.Position;
int length = (int)(src.Length - src.Position) + pos;
dest.SetLength(length);
while(pos < length)
pos += src.Read(dest.GetBuffer(), pos, length - pos);
}
else
src.CopyTo((Stream)dest);
}
&quot; CopyStream&quot;の実装を区別する基本的な質問
- 読み取りバッファのサイズ
- 書き込みのサイズ
- 複数のスレッドを使用できます(読み取り中に書き込みを行います)。
これらの質問に対する答えは、CopyStreamの非常に異なる実装をもたらし、使用するストリームの種類と最適化しようとしているものに依存します。 「最高の」実装では、ストリームが読み書きする特定のハードウェアを知る必要さえあります。
実際には、ストリームコピーを行うための手間がかからない方法があります。ただし、これはファイル全体をメモリに保存できることを意味することに注意してください。数百メガバイト以上のファイルを扱う場合は、注意せずにこれを使用しないでください。
public static void CopyStream(Stream input, Stream output)
{
using (StreamReader reader = new StreamReader(input))
using (StreamWriter writer = new StreamWriter(output))
{
writer.Write(reader.ReadToEnd());
}
}
注:バイナリデータと文字エンコーディングに関する問題もある可能性があります。
.NET Framework 4では、新しい&quot; CopyTo&quot;が導入されました。 System.IO名前空間のStreamクラスのメソッド。このメソッドを使用すると、あるストリームを異なるストリームクラスの別のストリームにコピーできます。
これの例を次に示します。
FileStream objFileStream = File.Open(Server.MapPath("TextFile.txt"), FileMode.Open);
Response.Write(string.Format("FileStream Content length: {0}", objFileStream.Length.ToString()));
MemoryStream objMemoryStream = new MemoryStream();
// Copy File Stream to Memory Stream using CopyTo method
objFileStream.CopyTo(objMemoryStream);
Response.Write("<br/><br/>");
Response.Write(string.Format("MemoryStream Content length: {0}", objMemoryStream.Length.ToString()));
Response.Write("<br/><br/>");
残念ながら、本当に簡単な解決策はありません。次のようなものを試すことができます:
Stream s1, s2;
byte[] buffer = new byte[4096];
int bytesRead = 0;
while (bytesRead = s1.Read(buffer, 0, buffer.Length) > 0) s2.Write(buffer, 0, bytesRead);
s1.Close(); s2.Close();
しかし、Streamクラスの異なる実装は、読み取るものがない場合に異なる動作をする可能性があるという問題があります。ローカルハードドライブからファイルを読み取るストリームは、読み取り操作がディスクから十分なデータを読み取ってバッファを満たすまでブロックし、ファイルの終わりに達した場合に返すデータが少なくなる可能性があります。一方、ネットワークから読み取るストリームは、受信するデータが残っている場合でも、返されるデータが少ない場合があります。
汎用ソリューションを使用する前に、使用している特定のストリームクラスのドキュメントを常に確認してください。
使用しているストリームの種類によっては、これをより効率的に行う方法があります。ストリームの一方または両方をMemoryStreamに変換できる場合、GetBufferメソッドを使用して、データを表すバイト配列を直接操作できます。これにより、Array.CopyToなどのメソッドを使用して、fryguybobによって提起されたすべての問題を抽象化できます。 .NETを信頼して、データをコピーする最適な方法を知ることができます。
ニックネームが投稿したストリームを他のストリームにコピーしたい場合は、位置のリセットが欠落しているはずです
public static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[32768];
long TempPos = input.Position;
while (true)
{
int read = input.Read (buffer, 0, buffer.Length);
if (read <= 0)
return;
output.Write (buffer, 0, read);
}
input.Position = TempPos;// or you make Position = 0 to set it at the start
}
ただし、実行時にプロシージャを使用していない場合は、メモリストリームを使用します
Stream output = new MemoryStream();
byte[] buffer = new byte[32768]; // or you specify the size you want of your buffer
long TempPos = input.Position;
while (true)
{
int read = input.Read (buffer, 0, buffer.Length);
if (read <= 0)
return;
output.Write (buffer, 0, read);
}
input.Position = TempPos;// or you make Position = 0 to set it at the start
1つのストリームから別のストリームにコピーする非同期の方法を扱った回答はないため、ここでは、あるネットワークストリームから別のネットワークストリームにデータをコピーするポート転送アプリケーションで正常に使用したパターンを示します。パターンを強調するための例外処理がありません。
const int BUFFER_SIZE = 4096;
static byte[] bufferForRead = new byte[BUFFER_SIZE];
static byte[] bufferForWrite = new byte[BUFFER_SIZE];
static Stream sourceStream = new MemoryStream();
static Stream destinationStream = new MemoryStream();
static void Main(string[] args)
{
// Initial read from source stream
sourceStream.BeginRead(bufferForRead, 0, BUFFER_SIZE, BeginReadCallback, null);
}
private static void BeginReadCallback(IAsyncResult asyncRes)
{
// Finish reading from source stream
int bytesRead = sourceStream.EndRead(asyncRes);
// Make a copy of the buffer as we'll start another read immediately
Array.Copy(bufferForRead, 0, bufferForWrite, 0, bytesRead);
// Write copied buffer to destination stream
destinationStream.BeginWrite(bufferForWrite, 0, bytesRead, BeginWriteCallback, null);
// Start the next read (looks like async recursion I guess)
sourceStream.BeginRead(bufferForRead, 0, BUFFER_SIZE, BeginReadCallback, null);
}
private static void BeginWriteCallback(IAsyncResult asyncRes)
{
// Finish writing to destination stream
destinationStream.EndWrite(asyncRes);
}
.NET 3.5以前の場合:
MemoryStream1.WriteTo(MemoryStream2);
簡単で安全-元のソースから新しいストリームを作成します:
MemoryStream source = new MemoryStream(byteArray);
MemoryStream copy = new MemoryStream(byteArray);