Mungkin yang lebih penting pada titik ini, bagaimana Anda menyalin konten "secara streaming", artinya hanya menyalin aliran sumber ketika sesuatu mengkonsumsi aliran tujuan ...?
Ini akan mengembalikan Taskyang 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 CopyToAsyncdibuat, kode yang mengikuti mungkin atau tidak dapat melanjutkan pada utas yang sama yang memanggilnya.
Yang SynchronizationContextditangkap saat memanggil awaitakan 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).
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.
Perhatikan bahwa ini bukan cara tercepat untuk melakukannya. Dalam cuplikan kode yang disediakan, Anda harus menunggu Tulis selesai sebelum blok baru dibaca. Ketika melakukan Baca dan Tulis secara tidak sinkron, penantian ini akan hilang. Dalam beberapa situasi ini akan membuat salinan dua kali lebih cepat. Namun itu akan membuat kode jauh lebih rumit sehingga jika kecepatan tidak menjadi masalah, buatlah tetap sederhana dan gunakan loop sederhana ini. Pertanyaan tentang StackOverflow ini memiliki beberapa kode yang menggambarkan async Baca / Tulis: stackoverflow.com/questions/1540658/... Salam, Sebastiaan
Sebastiaan M
16
FWIW, dalam pengujian saya, saya menemukan bahwa 4096 sebenarnya lebih cepat dari 32 ribu. Ada hubungannya dengan bagaimana CLR mengalokasikan potongan di atas ukuran tertentu. Karena itu, implementasi .NET dari Stream.CopyTo tampaknya menggunakan 4096.
Jeff
1
Jika Anda ingin tahu bagaimana CopyToAsync diimplementasikan atau membuat modifikasi seperti yang saya lakukan (saya harus dapat menentukan jumlah maksimum byte yang akan disalin) maka tersedia sebagai CopyStreamToStreamAsync di "Sampel untuk Pemrograman Paralel dengan .NET Framework" code.msdn .microsoft.com / ParExtSamples
Michael
1
FIY, ukuran buffer optimal 81920bytes, bukan32768
Alex Zhukovskiy
2
@ Jeff referecnceSource terbaru menunjukkan bahwa itu benar-benar menggunakan 81.920 byte penyangga.
Alex Zhukovskiy
66
MemoryStream memiliki .WriteTo (outstream);
dan .NET 4.0 memiliki .CopyTo pada objek stream normal.
Saya tidak melihat banyak sampel di web menggunakan metode ini. Apakah ini karena mereka cukup baru atau ada beberapa batasan?
GeneS
3
Itu karena mereka baru di. NET 4.0. Stream.CopyTo () pada dasarnya melakukan hal yang persis sama untuk loop yang jawaban yang disetujui lakukan, dengan beberapa pemeriksaan kewarasan tambahan. Ukuran buffer default adalah 4096, tetapi ada juga kelebihan untuk menentukan yang lebih besar.
Michael Edenfield
9
Aliran perlu diputar ulang setelah salinan: instream.Position = 0;
Draykos
6
Selain memundurkan aliran input, saya juga menemukan kebutuhan untuk memundurkan arus keluaran: outstream.Position = 0;
JonH
32
Saya menggunakan metode ekstensi berikut. Mereka telah mengoptimalkan kelebihan saat ketika satu aliran adalah MemoryStream.
Pertanyaan dasar yang membedakan implementasi "CopyStream" adalah:
ukuran buffer membaca
ukuran tulisan
Bisakah kita menggunakan lebih dari satu utas (menulis saat kita membaca).
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.
... atau implementasi terbaik dapat mengalami kelebihan beban untuk memungkinkan Anda menentukan ukuran buffer, ukuran tulis, dan apakah utas diizinkan?
MarkJ
1
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.
publicstaticvoidCopyStream(Stream input,Stream output){
using (StreamReader reader =newStreamReader(input))
using (StreamWriter writer =newStreamWriter(output)){
writer.Write(reader.ReadToEnd());}}
CATATAN: Mungkin juga ada beberapa masalah tentang data biner dan pengkodean karakter.
Konstruktor default untuk StreamWriter membuat aliran UTF8 tanpa BOM ( msdn.microsoft.com/en-us/library/fysy0a4b.aspx ) sehingga tidak ada bahaya masalah penyandian. Data biner hampir pasti tidak boleh disalin dengan cara ini.
keͣmͮpͥ ͩ
14
orang dapat dengan mudah berpendapat bahwa memuat "seluruh file dalam memori" hampir tidak dianggap "kurang berat".
Seph
saya mendapatkan pengecualian karena ini
ColacX
Ini bukan streaming untuk streaming. reader.ReadToEnd()menempatkan semuanya dalam RAM
Bizhan
1
.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.
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.
Solusi generik akan bekerja di sini - jawaban Nick adalah solusi yang bagus. Ukuran buffer tentu saja merupakan pilihan sewenang-wenang, tetapi 32K terdengar masuk akal. Saya pikir solusi Nick benar untuk tidak menutup aliran - serahkan itu kepada pemilik.
Jon Skeet
0
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
publicstaticvoidCopyStream(Stream input,Stream output){byte[] buffer =newbyte[32768];longTempPos= 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 =newMemoryStream();byte[] buffer =newbyte[32768];// or you specify the size you want of your bufferlongTempPos= 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
Anda tidak boleh mengubah posisi aliran input, karena tidak semua aliran mengizinkan akses acak. Dalam aliran jaringan, misalnya, Anda tidak dapat mengubah posisi, hanya membaca dan / atau menulis.
R. Martinho Fernandes
0
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.
constint BUFFER_SIZE =4096;staticbyte[] bufferForRead =newbyte[BUFFER_SIZE];staticbyte[] bufferForWrite =newbyte[BUFFER_SIZE];staticStream sourceStream =newMemoryStream();staticStream destinationStream =newMemoryStream();staticvoidMain(string[] args){// Initial read from source stream
sourceStream.BeginRead(bufferForRead,0, BUFFER_SIZE,BeginReadCallback,null);}privatestaticvoidBeginReadCallback(IAsyncResult asyncRes){// Finish reading from source streamint bytesRead = sourceStream.EndRead(asyncRes);// Make a copy of the buffer as we'll start another read immediatelyArray.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);}privatestaticvoidBeginWriteCallback(IAsyncResult asyncRes){// Finish writing to destination stream
destinationStream.EndWrite(asyncRes);}
Tentunya jika pembacaan kedua selesai sebelum penulisan pertama maka Anda akan menulis lebih dari isi bufferForWrite dari pembacaan pertama, sebelum ditulis.
Jawaban:
Dari .NET 4.5 pada, ada
Stream.CopyToAsync
metodeIni akan mengembalikan
Task
yang dapat dilanjutkan saat selesai, seperti: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 memanggilawait
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
metodeUntuk .NET 3.5 dan sebelumnya
Tidak ada yang dimasukkan ke dalam kerangka kerja untuk membantu dengan ini; Anda harus menyalin konten secara manual, seperti:
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 :sumber
81920
bytes, bukan32768
MemoryStream memiliki .WriteTo (outstream);
dan .NET 4.0 memiliki .CopyTo pada objek stream normal.
.NET 4.0:
sumber
Saya menggunakan metode ekstensi berikut. Mereka telah mengoptimalkan kelebihan saat ketika satu aliran adalah MemoryStream.
sumber
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.
sumber
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.
CATATAN: Mungkin juga ada beberapa masalah tentang data biner dan pengkodean karakter.
sumber
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.
sumber
CopyToAsync()
dianjurkan menggunakan .Sayangnya, tidak ada solusi yang sangat sederhana. Anda dapat mencoba sesuatu seperti itu:
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.
sumber
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.
sumber
jika Anda ingin procdure untuk menyalin aliran ke yang lain yang nick diposting baik-baik saja tetapi tidak ada posisi reset, itu harus
tetapi jika dalam runtime tidak menggunakan prosedur Anda shpuld menggunakan aliran memori
sumber
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.
sumber
Untuk .NET 3.5 dan sebelum mencoba:
sumber
Mudah dan aman - buat streaming baru dari sumber asli:
sumber