एक स्ट्रीम की सामग्री को दूसरे में कॉपी करने का सबसे अच्छा तरीका क्या है? क्या इसके लिए एक मानक उपयोगिता विधि है?
एक स्ट्रीम की सामग्री को दूसरे में कॉपी करने का सबसे अच्छा तरीका क्या है? क्या इसके लिए एक मानक उपयोगिता विधि है?
जवाबों:
.NET 4.5 पर, Stream.CopyToAsync
विधि है
input.CopyToAsync(output);
यह Task
पूरा हो जाएगा जब जारी रखा जा सकता है, जैसे कि:
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
? क्योंकि वह लंबाई उपलब्ध नहीं हो सकती है! से डॉक्स :
यदि स्ट्रीम से प्राप्त एक वर्ग मांग का समर्थन नहीं करता है, तो लंबाई, SetLength, स्थिति पर कॉल करें, और NotSupportedException को फेंक दें।
81920
बाइट्स, नहीं32768
मेमोरीस्ट्रीम में .WriteTo (आउटस्ट्रीम) है;
और .NET 4.0 में सामान्य स्ट्रीम ऑब्जेक्ट पर .CopyTo है।
.NET 4.0:
instream.CopyTo(outstream);
मैं निम्नलिखित विस्तार विधियों का उपयोग करता हूं। जब एक स्ट्रीम एक मेमोरीस्ट्रीम है, तो उन्होंने ओवरलोड को अनुकूलित किया है।
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" के कार्यान्वयन में अंतर करने वाले मूल प्रश्न हैं:
इन सवालों के जवाब में कॉपीस्ट्रीम के अलग-अलग कार्यान्वयन होते हैं और यह निर्भर करता है कि आपके पास किस तरह की धाराएँ हैं और आप क्या अनुकूलन करने की कोशिश कर रहे हैं। "सर्वश्रेष्ठ" कार्यान्वयन को यह जानने की भी आवश्यकता होगी कि धाराएँ किन विशिष्ट हार्डवेयरों को पढ़ और लिख रही थीं।
वास्तव में, स्ट्रीम कॉपी करने का कम भारी हाथ वाला तरीका है। हालाँकि ध्यान दें, इसका मतलब यह है कि आप पूरी फ़ाइल को मेमोरी में स्टोर कर सकते हैं। यदि आप बिना सावधानी के सैकड़ों मेगाबाइट या उससे अधिक की दूरी पर जाने वाली फ़ाइलों के साथ काम कर रहे हैं, तो इसे आज़माएँ और इसका उपयोग न करें।
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()
सब कुछ राम में डालता है
.NET फ्रेमवर्क 4, System.IO नाम स्थान की स्ट्रीम क्लास की नई "कॉपीटो" विधि का परिचय देता है। इस पद्धति का उपयोग करके हम एक स्ट्रीम को अलग स्ट्रीम क्लास की दूसरी स्ट्रीम में कॉपी कर सकते हैं।
यहाँ इसके लिए उदाहरण दिया गया है।
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();
लेकिन इसके साथ समस्या यह है कि अगर पढ़ने के लिए कुछ नहीं है, तो स्ट्रीम क्लास के अलग-अलग कार्यान्वयन अलग तरीके से व्यवहार कर सकते हैं। एक स्थानीय हार्डड्राइव से एक फ़ाइल पढ़ने वाली धारा संभवतः तब तक ब्लॉक रहेगी जब तक कि रीड ऑप्रेशन ने बफर को भरने के लिए डिस्क से पर्याप्त डेटा नहीं पढ़ा हो और फ़ाइल के अंत तक पहुंचने पर केवल कम डेटा लौटाया हो। दूसरी ओर, नेटवर्क से पढ़ने वाली एक स्ट्रीम कम डेटा लौटा सकती है, जबकि अधिक डेटा प्राप्त होना बाकी है।
हमेशा जेनेरिक समाधान का उपयोग करने से पहले आपके द्वारा उपयोग किए जा रहे विशिष्ट स्ट्रीम वर्ग के दस्तावेज़ीकरण की जांच करें।
आप किस तरह की स्ट्रीम के साथ काम कर रहे हैं, इसके आधार पर इसे और अधिक कुशलता से करने का एक तरीका हो सकता है। यदि आप मेमोरी स्ट्रीम में अपनी एक या दोनों धाराओं को परिवर्तित कर सकते हैं, तो आप अपने डेटा का प्रतिनिधित्व करने वाली बाइट सरणी के साथ सीधे काम करने के लिए गेटबफर विधि का उपयोग कर सकते हैं। यह आपको Array.CopyTo जैसे तरीकों का उपयोग करने देता है, जो फ्राइगुयॉब द्वारा उठाए गए सभी मुद्दों को दूर करता है। डेटा को कॉपी करने का इष्टतम तरीका जानने के लिए आप सिर्फ .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
चूंकि किसी भी उत्तर ने एक स्ट्रीम से दूसरे स्ट्रीम में कॉपी करने के अतुल्यकालिक तरीके को कवर नहीं किया है, यहां एक पैटर्न है जिसे मैंने एक नेटवर्क स्ट्रीम से दूसरे में डेटा कॉपी करने के लिए पोर्ट फॉरवर्डिंग एप्लिकेशन में सफलतापूर्वक उपयोग किया है। इसमें पैटर्न पर जोर देने के लिए अपवाद हैंडलिंग का अभाव है।
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);
}
आसान और सुरक्षित - मूल स्रोत से नई धारा बनाएं:
MemoryStream source = new MemoryStream(byteArray);
MemoryStream copy = new MemoryStream(byteArray);