วิธีที่ดีที่สุดในการคัดลอกเนื้อหาของสตรีมหนึ่งไปยังอีกสตรีมคืออะไร? มีวิธียูทิลิตี้มาตรฐานสำหรับสิ่งนี้หรือไม่?
วิธีที่ดีที่สุดในการคัดลอกเนื้อหาของสตรีมหนึ่งไปยังอีกสตรีมคืออะไร? มีวิธียูทิลิตี้มาตรฐานสำหรับสิ่งนี้หรือไม่?
คำตอบ:
จาก. NET 4.5 บนมีStream.CopyToAsync
วิธีการ
input.CopyToAsync(output);
สิ่งนี้จะคืนค่า a Task
ที่สามารถดำเนินการต่อได้เมื่อดำเนินการเสร็จแล้วเช่น:
await input.CopyToAsync(output)
// Code from here on will be run in a continuation.
โปรดทราบว่าขึ้นอยู่กับตำแหน่งที่โทรไปCopyToAsync
รหัสที่ตามมาอาจจะหรืออาจไม่ดำเนินการกับเธรดเดียวกับที่เรียกว่า
SynchronizationContext
ที่ถูกจับเมื่อโทรawait
จะตรวจสอบสิ่งที่ด้ายต่อเนื่องจะได้รับการดำเนินการใน
นอกจากนี้การเรียกนี้ (และนี่คือการเปลี่ยนแปลงรายละเอียดการนำไปใช้) ยังคงเป็นลำดับอ่านและเขียน
จาก. 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
? เพราะความยาวนั้นอาจไม่สามารถใช้ได้! จากเอกสาร :
ถ้าคลาสที่ได้รับจากสตรีมไม่รองรับการค้นหาให้โทรไปที่ความยาว, SetLength, ตำแหน่งและค้นหาการโยน NotSupportedException
81920
ไบต์ไม่ใช่32768
MemoryStream มี .WriteTo (outstream);
และ. NET 4.0 มี. คัดลอกไปยังวัตถุกระแสข้อมูลปกติ
.NET 4.0:
instream.CopyTo(outstream);
ฉันใช้วิธีการต่อไปนี้ พวกเขาได้ปรับปรุงการโอเวอร์โหลดเมื่อสตรีมเดียวคือ 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);
}
คำถามพื้นฐานที่แยกความแตกต่างของการใช้งานของ "CopyStream" คือ:
คำตอบสำหรับคำถามเหล่านี้ส่งผลให้เกิดการใช้งานที่แตกต่างกันอย่างมากมายของ 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());
}
}
หมายเหตุ: อาจมีปัญหาบางอย่างเกี่ยวกับข้อมูลไบนารีและการเข้ารหัสอักขระ
reader.ReadToEnd()
ใส่ทุกอย่างใน RAM
.NET Framework 4 แนะนำวิธีการ "CopyTo" ใหม่ของ Stream Class ของ System.IO namespace การใช้วิธีการนี้เราสามารถคัดลอกสตรีมหนึ่งไปยังสตรีมอื่นของคลาสสตรีมอื่น
นี่คือตัวอย่างสำหรับสิ่งนี้
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()
สนับสนุนให้ใช้
น่าเสียดายที่ไม่มีวิธีแก้ปัญหาง่ายๆ คุณสามารถลองทำสิ่งต่อไปนี้:
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 เพื่อทราบวิธีที่ดีที่สุดในการคัดลอกข้อมูล
หากคุณต้องการให้ procdure คัดลอกสตรีมไปยังสตรีมอื่นที่ Nick โพสต์นั้นใช้ได้ แต่หายไปจากการรีเซ็ตตำแหน่งมันควรจะเป็น
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
}
แต่ถ้ามันอยู่ในรันไทม์ไม่ได้ใช้โพรซีเดอร์คุณ shpuld ใช้หน่วยความจำสตรีม
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
เนื่องจากไม่มีคำตอบใดที่ครอบคลุมวิธีคัดลอกแบบอะซิงโครนัสจากสตรีมหนึ่งไปยังสตรีมอื่นนี่เป็นรูปแบบที่ฉันใช้ในแอปพลิเคชันการส่งต่อพอร์ตเพื่อคัดลอกข้อมูลจากสตรีมเครือข่ายหนึ่งไปยังอีก มันไม่มีข้อยกเว้นในการจัดการเพื่อเน้นรูปแบบ
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);