मैं एक स्ट्रीम की सामग्री को दूसरे में कैसे कॉपी करूं?


521

एक स्ट्रीम की सामग्री को दूसरे में कॉपी करने का सबसे अच्छा तरीका क्या है? क्या इसके लिए एक मानक उपयोगिता विधि है?


शायद इस बिंदु पर अधिक महत्वपूर्ण बात, आप सामग्री को "स्ट्रीमली" कैसे कॉपी करते हैं, इसका मतलब है कि यह केवल स्रोत स्ट्रीम को कॉपी करता है क्योंकि कुछ गंतव्य स्ट्रीम का उपभोग करता है ...?
drzaus

जवाबों:


694

.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 को फेंक दें।


58
ध्यान दें कि यह इसे करने का सबसे तेज़ तरीका नहीं है। प्रदान किए गए कोड स्निपेट में, आपको नया ब्लॉक पढ़ने से पहले लिखने के लिए पूरा होने का इंतजार करना होगा। जब एसिंक्रोनस रूप से पढ़ें और लिखें तो यह प्रतीक्षा गायब हो जाएगी। कुछ स्थिति में यह कॉपी को दोगुना तेज कर देगा। हालाँकि यह कोड को और अधिक जटिल बना देगा इसलिए यदि गति कोई समस्या नहीं है, तो इसे सरल रखें और इस सरल लूप का उपयोग करें। StackOverflow पर इस प्रश्न का कुछ कोड है जो async को दिखाता है / पढ़ें: stackoverflow.com/questions/1540658/… सादर, सेबेस्टियन
सेबेस्टियन M

16
एफडब्ल्यूआईडब्ल्यू, मेरे परीक्षण में मैंने पाया है कि 4096 वास्तव में 32K से तेज है। सीएलआर कैसे एक निश्चित आकार से अधिक मात्रा आवंटित करता है, इसके साथ कुछ करना है। इसके कारण, Stream.CopyTo का .NET कार्यान्वयन जाहिरा तौर पर 4096 का उपयोग करता है।
जेफ

1
यदि आप यह जानना चाहते हैं कि CopyToAsync कैसे लागू किया जाता है या मैंने जैसे संशोधन किए हैं (मुझे कॉपी करने के लिए अधिकतम बाइट्स निर्दिष्ट करने में सक्षम होने की आवश्यकता है) तो यह CopyStreamToStreamAsync के रूप में उपलब्ध है ".NET फ्रेमवर्क के साथ समानांतर प्रोग्राम के लिए नमूने" code.msdn .microsoft.com / ParExtSamples
माइकल

1
FIY, इष्टतम बफर आकार iz 81920बाइट्स, नहीं32768
एलेक्स

2
@Jeff का नवीनतम रेफ़रेन्स स्रोत बताता है कि यह वास्तव में 81920 बाइट्स बफर का उपयोग करता है।
एलेक्स ज़ुकोवस्की

66

मेमोरीस्ट्रीम में .WriteTo (आउटस्ट्रीम) है;

और .NET 4.0 में सामान्य स्ट्रीम ऑब्जेक्ट पर .CopyTo है।

.NET 4.0:

instream.CopyTo(outstream);

मैं इन तरीकों का उपयोग करके वेब पर कई नमूने नहीं देखता हूं। क्या यह इसलिए है क्योंकि वे काफी नए हैं या कुछ सीमाएं हैं?
१ .

3
ऐसा इसलिए है क्योंकि वे .NET 4.0 में नए हैं। Stream.CopyTo () मूल रूप से लूप के लिए वही करता है जो स्वीकृत उत्तर कुछ अतिरिक्त संचित जांचों के साथ करता है। डिफ़ॉल्ट बफ़र का आकार 4096 है, लेकिन एक बड़ा निर्दिष्ट करने के लिए एक अधिभार भी है।
माइकल ईडनफील्ड

9
कॉपी के बाद स्ट्रीम को रिवाइंड करना होगा: instream.Position = 0;
ड्रायकोस

6
इनपुट स्ट्रीम को रिवाइंड करने के अलावा, मुझे आउटपुट स्ट्रीम को रिवाइंड करने की भी जरूरत पड़ी: आउटस्ट्रीम।पोजिशन = 0;
जॉन एचएच

32

मैं निम्नलिखित विस्तार विधियों का उपयोग करता हूं। जब एक स्ट्रीम एक मेमोरीस्ट्रीम है, तो उन्होंने ओवरलोड को अनुकूलित किया है।

    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);
    }

1

"CopyStream" के कार्यान्वयन में अंतर करने वाले मूल प्रश्न हैं:

  • पठन बफर का आकार
  • लिखने का आकार
  • क्या हम एक से अधिक थ्रेड का उपयोग कर सकते हैं (लिखते समय हम पढ़ रहे हैं)।

इन सवालों के जवाब में कॉपीस्ट्रीम के अलग-अलग कार्यान्वयन होते हैं और यह निर्भर करता है कि आपके पास किस तरह की धाराएँ हैं और आप क्या अनुकूलन करने की कोशिश कर रहे हैं। "सर्वश्रेष्ठ" कार्यान्वयन को यह जानने की भी आवश्यकता होगी कि धाराएँ किन विशिष्ट हार्डवेयरों को पढ़ और लिख रही थीं।


1
... या सबसे अच्छा कार्यान्वयन ओवरलोड हो सकता है जिससे आप बफर आकार निर्दिष्ट कर सकें, आकार लिख सकें, और क्या धागे की अनुमति है?
MarkJ

1

वास्तव में, स्ट्रीम कॉपी करने का कम भारी हाथ वाला तरीका है। हालाँकि ध्यान दें, इसका मतलब यह है कि आप पूरी फ़ाइल को मेमोरी में स्टोर कर सकते हैं। यदि आप बिना सावधानी के सैकड़ों मेगाबाइट या उससे अधिक की दूरी पर जाने वाली फ़ाइलों के साथ काम कर रहे हैं, तो इसे आज़माएँ और इसका उपयोग न करें।

public static void CopyStream(Stream input, Stream output)
{
  using (StreamReader reader = new StreamReader(input))
  using (StreamWriter writer = new StreamWriter(output))
  {
    writer.Write(reader.ReadToEnd());
  }
}

नोट: बाइनरी डेटा और चरित्र एन्कोडिंग के विषय में कुछ समस्याएँ भी हो सकती हैं।


6
StreamWriter के लिए डिफ़ॉल्ट कंस्ट्रक्टर एक BOM ( msdn.microsoft.com/en-us/library/fysy0a4b.aspx ) के बिना UTF8 स्ट्रीम बनाता है ताकि एन्कोडिंग समस्याओं का कोई खतरा न हो। बाइनरी डेटा लगभग निश्चित रूप से इस तरह से कॉपी नहीं किया जाना चाहिए।
k --e'mͮpͥ ͩ

14
कोई भी आसानी से तर्क दे सकता है कि "मेमोरी में संपूर्ण फ़ाइल" लोड करना शायद ही "कम भारी-हाथ" माना जाता है।
सिपाही

मुझे इसकी वजह से बाहरी अपवाद मिलता है
ColacX

यह धारा प्रवाह नहीं है। reader.ReadToEnd()सब कुछ राम में डालता है
बिशन

1

.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()को प्रोत्साहित किया जाता है।
जरी तुर्किया

0

दुर्भाग्य से, वास्तव में कोई सरल उपाय नहीं है। आप कुछ इस तरह की कोशिश कर सकते हैं:

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();

लेकिन इसके साथ समस्या यह है कि अगर पढ़ने के लिए कुछ नहीं है, तो स्ट्रीम क्लास के अलग-अलग कार्यान्वयन अलग तरीके से व्यवहार कर सकते हैं। एक स्थानीय हार्डड्राइव से एक फ़ाइल पढ़ने वाली धारा संभवतः तब तक ब्लॉक रहेगी जब तक कि रीड ऑप्रेशन ने बफर को भरने के लिए डिस्क से पर्याप्त डेटा नहीं पढ़ा हो और फ़ाइल के अंत तक पहुंचने पर केवल कम डेटा लौटाया हो। दूसरी ओर, नेटवर्क से पढ़ने वाली एक स्ट्रीम कम डेटा लौटा सकती है, जबकि अधिक डेटा प्राप्त होना बाकी है।

हमेशा जेनेरिक समाधान का उपयोग करने से पहले आपके द्वारा उपयोग किए जा रहे विशिष्ट स्ट्रीम वर्ग के दस्तावेज़ीकरण की जांच करें।


5
जेनेरिक समाधान यहां काम करेगा - निक का जवाब ठीक है। बफर आकार बेशक एक मनमाना विकल्प है, लेकिन 32K उचित लगता है। मुझे लगता है कि निक का समाधान धाराओं को बंद करने के लिए सही नहीं है - हालांकि उस मालिक को छोड़ दें।
जॉन स्कीट

0

आप किस तरह की स्ट्रीम के साथ काम कर रहे हैं, इसके आधार पर इसे और अधिक कुशलता से करने का एक तरीका हो सकता है। यदि आप मेमोरी स्ट्रीम में अपनी एक या दोनों धाराओं को परिवर्तित कर सकते हैं, तो आप अपने डेटा का प्रतिनिधित्व करने वाली बाइट सरणी के साथ सीधे काम करने के लिए गेटबफर विधि का उपयोग कर सकते हैं। यह आपको Array.CopyTo जैसे तरीकों का उपयोग करने देता है, जो फ्राइगुयॉब द्वारा उठाए गए सभी मुद्दों को दूर करता है। डेटा को कॉपी करने का इष्टतम तरीका जानने के लिए आप सिर्फ .NET पर भरोसा कर सकते हैं।


0

यदि आप चाहते हैं कि किसी एक को दूसरे को स्ट्रीम कॉपी करने के लिए एक एनसीडी पोस्ट किया जाए तो वह ठीक है लेकिन यह स्थिति रीसेट को याद कर रहा है, यह होना चाहिए

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

3
आपको इनपुट स्ट्रीम की स्थिति नहीं बदलनी चाहिए, क्योंकि सभी स्ट्रीम यादृच्छिक अभिगम की अनुमति नहीं देते हैं। एक नेटवर्क स्ट्रीम में, उदाहरण के लिए, आप स्थिति नहीं बदल सकते, केवल पढ़ सकते हैं और / या लिख ​​सकते हैं।
आर। मार्टिनो फर्नांडीस

0

चूंकि किसी भी उत्तर ने एक स्ट्रीम से दूसरे स्ट्रीम में कॉपी करने के अतुल्यकालिक तरीके को कवर नहीं किया है, यहां एक पैटर्न है जिसे मैंने एक नेटवर्क स्ट्रीम से दूसरे में डेटा कॉपी करने के लिए पोर्ट फॉरवर्डिंग एप्लिकेशन में सफलतापूर्वक उपयोग किया है। इसमें पैटर्न पर जोर देने के लिए अपवाद हैंडलिंग का अभाव है।

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);
}

4
निश्चित रूप से अगर दूसरा लिखा पहला लिखने से पहले पूरा हो जाता है तो आप पहले पढ़े लिखे से बफर बफर की सामग्री पर लिखेंगे, इससे पहले कि यह लिखा जाए।
पीटर जेफरी

0

.NET 3.5 के लिए और प्रयास करने से पहले:

MemoryStream1.WriteTo(MemoryStream2);

यह तभी काम करता है जब आप मेमोरीस्ट्रीम के साथ काम कर रहे हों।
Nyerguds

0

आसान और सुरक्षित - मूल स्रोत से नई धारा बनाएं:

    MemoryStream source = new MemoryStream(byteArray);
    MemoryStream copy = new MemoryStream(byteArray);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.