Apa cara terbaik untuk menyalin konten dari satu aliran ke yang lain? Apakah ada metode utilitas standar untuk ini?
Apa cara terbaik untuk menyalin konten dari satu aliran ke yang lain? Apakah ada metode utilitas standar untuk ini?
Jawaban:
Dari .NET 4.5 pada, ada Stream.CopyToAsync
metode
input.CopyToAsync(output);
Ini akan mengembalikan Task
yang dapat dilanjutkan saat selesai, seperti:
await input.CopyToAsync(output)
// Code from here on will be run in a continuation.
Perhatikan bahwa tergantung pada tempat panggilan CopyToAsync
dibuat, kode yang mengikuti mungkin atau tidak dapat melanjutkan pada utas yang sama yang memanggilnya.
Yang SynchronizationContext
ditangkap saat memanggil await
akan menentukan utas kelanjutan yang akan dieksekusi.
Selain itu, panggilan ini (dan ini adalah detail implementasi yang dapat berubah) masih berurutan membaca dan menulis (hanya saja tidak membuang utas yang memblokir penyelesaian I / O).
Dari .NET 4.0 pada, ada Stream.CopyTo
metode
input.CopyTo(output);
Untuk .NET 3.5 dan sebelumnya
Tidak ada yang dimasukkan ke dalam kerangka kerja untuk membantu dengan ini; Anda harus menyalin konten secara manual, seperti:
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);
}
}
Catatan 1: Metode ini akan memungkinkan Anda untuk melaporkan kemajuan (baca x byte sejauh ini ...)
Catatan 2: Mengapa menggunakan ukuran buffer tetap dan tidak input.Length
? Karena Panjang itu mungkin tidak tersedia! Dari dokumen :
Jika kelas yang berasal dari Stream tidak mendukung pencarian, panggilan ke Panjang, SetLength, Posisi, dan Cari melempar NotSupportedException.
81920
bytes, bukan32768
MemoryStream memiliki .WriteTo (outstream);
dan .NET 4.0 memiliki .CopyTo pada objek stream normal.
.NET 4.0:
instream.CopyTo(outstream);
Saya menggunakan metode ekstensi berikut. Mereka telah mengoptimalkan kelebihan saat ketika satu aliran adalah 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);
}
Pertanyaan dasar yang membedakan implementasi "CopyStream" adalah:
Jawaban atas pertanyaan-pertanyaan ini menghasilkan implementasi yang sangat berbeda dari CopyStream dan bergantung pada jenis aliran apa yang Anda miliki dan apa yang Anda coba optimalkan. Implementasi "terbaik" bahkan perlu tahu perangkat keras spesifik apa yang dibaca dan ditulis stream.
Sebenarnya, ada cara yang tidak terlalu berat dalam melakukan copy aliran. Namun perhatikan, ini menunjukkan bahwa Anda dapat menyimpan seluruh file dalam memori. Jangan coba dan gunakan ini jika Anda bekerja dengan file yang masuk ke ratusan megabyte atau lebih, tanpa hati-hati.
public static void CopyStream(Stream input, Stream output)
{
using (StreamReader reader = new StreamReader(input))
using (StreamWriter writer = new StreamWriter(output))
{
writer.Write(reader.ReadToEnd());
}
}
CATATAN: Mungkin juga ada beberapa masalah tentang data biner dan pengkodean karakter.
reader.ReadToEnd()
menempatkan semuanya dalam RAM
.NET Framework 4 memperkenalkan metode "CopyTo" baru dari Stream Class of System.IO namespace. Dengan menggunakan metode ini kita dapat menyalin satu aliran ke aliran lain dari kelas aliran yang berbeda.
Ini adalah contoh untuk ini.
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/>");
CopyToAsync()
dianjurkan menggunakan .
Sayangnya, tidak ada solusi yang sangat sederhana. Anda dapat mencoba sesuatu seperti itu:
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();
Tetapi masalah dengan implementasi yang berbeda dari kelas Stream mungkin berperilaku berbeda jika tidak ada yang dibaca. Aliran yang membaca file dari hard drive lokal mungkin akan diblokir sampai operasi baca telah membaca cukup data dari disk untuk mengisi buffer dan hanya mengembalikan lebih sedikit data jika mencapai akhir file. Di sisi lain, pembacaan aliran dari jaringan mungkin mengembalikan lebih sedikit data meskipun ada lebih banyak data yang tersisa untuk diterima.
Selalu periksa dokumentasi kelas aliran spesifik yang Anda gunakan sebelum menggunakan solusi generik.
Mungkin ada cara untuk melakukan ini dengan lebih efisien, tergantung pada jenis aliran yang Anda gunakan. Jika Anda dapat mengonversi satu atau kedua aliran Anda ke MemoryStream, Anda dapat menggunakan metode GetBuffer untuk bekerja secara langsung dengan array byte yang mewakili data Anda. Ini memungkinkan Anda menggunakan metode seperti Array.CopyTo, yang memisahkan semua masalah yang diangkat oleh fryguybob. Anda bisa mempercayai .NET untuk mengetahui cara optimal untuk menyalin data.
jika Anda ingin procdure untuk menyalin aliran ke yang lain yang nick diposting baik-baik saja tetapi tidak ada posisi reset, itu harus
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
}
tetapi jika dalam runtime tidak menggunakan prosedur Anda shpuld menggunakan aliran memori
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
Karena tidak ada jawaban yang membahas cara menyalin secara tidak sinkron dari satu aliran ke aliran lain, berikut adalah pola yang saya berhasil gunakan dalam aplikasi penerusan port untuk menyalin data dari satu aliran jaringan ke aliran yang lain. Tidak ada penanganan pengecualian untuk menekankan pola.
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);
}
Mudah dan aman - buat streaming baru dari sumber asli:
MemoryStream source = new MemoryStream(byteArray);
MemoryStream copy = new MemoryStream(byteArray);