Cách tốt nhất để sao chép nội dung của luồng này sang luồng khác là gì? Có một phương pháp tiện ích tiêu chuẩn cho việc này?
Cách tốt nhất để sao chép nội dung của luồng này sang luồng khác là gì? Có một phương pháp tiện ích tiêu chuẩn cho việc này?
Câu trả lời:
Từ .NET 4.5 trở đi, có Stream.CopyToAsync
phương thức
input.CopyToAsync(output);
Điều này sẽ trả về một Task
có thể được tiếp tục khi hoàn thành, như vậy:
await input.CopyToAsync(output)
// Code from here on will be run in a continuation.
Lưu ý rằng tùy thuộc vào nơi cuộc gọi CopyToAsync
được thực hiện, mã theo sau có thể hoặc không thể tiếp tục trên cùng một chuỗi đã gọi nó.
Cái SynchronizationContext
được chụp khi gọi await
sẽ xác định luồng nào sẽ tiếp tục được thực hiện.
Ngoài ra, cuộc gọi này (và đây là một chi tiết triển khai có thể thay đổi) vẫn tiếp tục đọc và ghi (nó chỉ không lãng phí một chuỗi chặn khi hoàn thành I / O).
Từ .NET 4.0 trở đi, có Stream.CopyTo
phương thức
input.CopyTo(output);
Đối với .NET 3.5 trở về trước
Không có bất cứ điều gì được đưa vào khuôn khổ để hỗ trợ việc này; bạn phải sao chép nội dung theo cách thủ công, như vậy:
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);
}
}
Lưu ý 1: Phương pháp này sẽ cho phép bạn báo cáo về tiến trình (x byte đọc cho đến nay ...)
Lưu ý 2: Tại sao nên sử dụng kích thước bộ đệm cố định mà không phải input.Length
? Bởi vì chiều dài đó có thể không có sẵn! Từ các tài liệu :
Nếu một lớp có nguồn gốc từ Luồng không hỗ trợ tìm kiếm, hãy gọi đến Độ dài, Đặt độ dài, Vị trí và Tìm kiếm ném một ngoại lệ NotSupportedException.
81920
byte, không phải32768
MemoryStream có .WriteTo (ngoài luồng);
và .NET 4.0 có .CopyTo trên đối tượng luồng bình thường.
.NET 4.0:
instream.CopyTo(outstream);
Tôi sử dụng các phương pháp mở rộng sau đây. Họ đã tối ưu hóa quá tải khi một luồng là 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);
}
Các câu hỏi cơ bản phân biệt việc triển khai "CopyStream" là:
Các câu trả lời cho những câu hỏi này dẫn đến việc triển khai CopyStream rất khác nhau và phụ thuộc vào loại luồng bạn có và những gì bạn đang cố gắng tối ưu hóa. Việc triển khai "tốt nhất" thậm chí sẽ cần biết phần cứng cụ thể mà các luồng đang đọc và ghi vào.
Thực sự, có một cách ít nặng nề hơn để thực hiện một bản sao luồng. Tuy nhiên, hãy lưu ý rằng điều này ngụ ý rằng bạn có thể lưu trữ toàn bộ tệp trong bộ nhớ. Đừng thử và sử dụng điều này nếu bạn đang làm việc với các tệp đi vào hàng trăm megabyte trở lên, không cần thận trọng.
public static void CopyStream(Stream input, Stream output)
{
using (StreamReader reader = new StreamReader(input))
using (StreamWriter writer = new StreamWriter(output))
{
writer.Write(reader.ReadToEnd());
}
}
LƯU Ý: Cũng có thể có một số vấn đề liên quan đến dữ liệu nhị phân và mã hóa ký tự.
reader.ReadToEnd()
đặt mọi thứ vào RAM
.NET Framework 4 giới thiệu phương thức "CopyTo" mới của lớp Stream của không gian tên System.IO. Sử dụng phương pháp này, chúng ta có thể sao chép một luồng sang luồng khác của lớp luồng khác.
Dưới đây là ví dụ cho điều này.
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()
được khuyến khích.
Thật không may, không có giải pháp thực sự đơn giản. Bạn có thể thử một cái gì đó như thế:
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();
Nhưng vấn đề với việc triển khai khác nhau của lớp Stream có thể hành xử khác đi nếu không có gì để đọc. Một luồng đọc tệp từ ổ cứng cục bộ có thể sẽ chặn cho đến khi hoạt động đọc đã đọc đủ dữ liệu từ đĩa để điền vào bộ đệm và chỉ trả lại ít dữ liệu hơn nếu đến cuối tệp. Mặt khác, việc đọc luồng từ mạng có thể trả về ít dữ liệu hơn mặc dù có nhiều dữ liệu còn lại để nhận.
Luôn kiểm tra tài liệu của lớp luồng cụ thể mà bạn đang sử dụng trước khi sử dụng giải pháp chung.
Có thể có một cách để làm điều này hiệu quả hơn, tùy thuộc vào loại luồng bạn đang làm việc. Nếu bạn có thể chuyển đổi một hoặc cả hai luồng của mình thành MemoryStream, bạn có thể sử dụng phương thức GetBuffer để làm việc trực tiếp với một mảng byte biểu thị dữ liệu của bạn. Điều này cho phép bạn sử dụng các phương thức như Array.CopyTo, giúp loại bỏ tất cả các vấn đề do fryguybob nêu ra. Bạn chỉ có thể tin tưởng .NET để biết cách tối ưu để sao chép dữ liệu.
nếu bạn muốn một procdure sao chép luồng này sang luồng khác mà nick đã đăng thì không sao nhưng nó lại bị mất vị trí thiết lập lại, nó nên
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
}
nhưng nếu trong thời gian chạy không sử dụng thủ tục, bạn nên sử dụng luồng bộ nhớ
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
Vì không có câu trả lời nào đề cập đến cách sao chép không đồng bộ từ luồng này sang luồng khác, nên đây là mẫu mà tôi đã sử dụng thành công trong ứng dụng chuyển tiếp cổng để sao chép dữ liệu từ luồng mạng này sang luồng khác. Nó thiếu xử lý ngoại lệ để nhấn mạnh mô hình.
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);
}
Dễ dàng và an toàn - tạo luồng mới từ nguồn ban đầu:
MemoryStream source = new MemoryStream(byteArray);
MemoryStream copy = new MemoryStream(byteArray);