আমি কীভাবে একটি স্ট্রিমের বিষয়বস্তুটিকে অন্য প্রবাহে অনুলিপি করব?


521

একটি স্ট্রিমের বিষয়বস্তু অন্য প্রবাহে অনুলিপি করার সর্বোত্তম উপায় কী? এর জন্য কি কোনও স্ট্যান্ডার্ড ইউটিলিটি পদ্ধতি আছে?


সম্ভবত আরও গুরুত্বপূর্ণ এই মুহুর্তে, আপনি কীভাবে "স্ট্রিম্যাবলি" বিষয়বস্তুগুলি অনুলিপি করবেন, যার অর্থ এটি কোনও গন্তব্য প্রবাহ গ্রাস করায় উত্স প্রবাহটি কেবল অনুলিপি করে ...?
ড্রজাউস

উত্তর:


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তার উপর নির্ভর করে , কোড অনুসরণ করা কোডটি একই থ্রেডে কল করতে পারে বা চালিয়ে যেতে পারে that

SynchronizationContextযে যখন কলিং বন্দী করা হয় awaitকি থ্রেড ধারাবাহিকতা উপর মৃত্যুদন্ড কার্যকর করা হবে নির্ধারণ করা হবে।

অতিরিক্তভাবে, এই কলটি (এবং এটি পরিবর্তনের সাপেক্ষে এটি বাস্তবায়নের বিশদ sequ

.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: এই পদ্ধতিটি আপনাকে অগ্রগতির প্রতিবেদন করতে অনুমতি দেবে (এক্স বাইটগুলি এখনও অবধি পড়ে ...)
দ্রষ্টব্য 2: একটি নির্দিষ্ট বাফার আকার কেন ব্যবহার করবেন না input.Length? কারণ যে দৈর্ঘ্য উপলব্ধ নাও হতে পারে! ডক্স থেকে :

স্ট্রিম থেকে প্রাপ্ত কোনও শ্রেণি যদি সন্ধানকে সমর্থন না করে তবে দৈর্ঘ্য, সেটলেন্থ, অবস্থান এবং কল করুন একটি নটসপোর্টড এক্সসেপশন নিক্ষেপ করুন।


58
দ্রষ্টব্য যে এটি করার দ্রুততম উপায় নয়। প্রদত্ত কোড স্নিপেটে, নতুন ব্লকটি পড়ার আগে আপনাকে লেখার সম্পূর্ণ হওয়ার জন্য অপেক্ষা করতে হবে। অবিচ্ছিন্নভাবে পড়ুন এবং লেখার সময় এই প্রত্যাশাটি অদৃশ্য হয়ে যাবে। কিছু পরিস্থিতিতে এটি অনুলিপিটিকে দ্বিগুণ দ্রুত করে তুলবে। তবে এটি কোডটিকে আরও জটিল করে তুলবে তাই যদি গতি কোনও সমস্যা না হয় তবে এটিকে সহজ রাখুন এবং এই সরল লুপটি ব্যবহার করুন। স্ট্যাকওভারফ্লোতে এই প্রশ্নের কিছু কোড রয়েছে যা অ্যাসিঙ্কটি পড়ুন / লিখুন: স্ট্যাকওভারফ্লো / প্রশ্ন / 1540658/… শুভেচ্ছা, সেবাসটিয়ান
সেবাস্তিয়ান এম

16
এফডাব্লুআইডাব্লু, আমার পরীক্ষায় আমি দেখতে পেয়েছি যে 4096 আসলে 32 কে-এর চেয়ে দ্রুত। সিএলআর কীভাবে একটি নির্দিষ্ট আকারের উপরে খণ্ডগুলি বরাদ্দ করে তার কিছু করার। এ কারণে, স্ট্রিম.কপিটিও .NET বাস্তবায়ন স্পষ্টতই 4096 ব্যবহার করে
জেফ

1
আপনাকে জানতে হবে কিভাবে CopyToAsync বাস্তবায়িত বা মত আমি পরিবর্তন করতে হয় চান, তাহলে এটি ".NET ফ্রেমওয়ার্ক সমান্তরাল প্রোগ্রামিং জন্য নমুনা" এ CopyStreamToStreamAsync যেমন উপলব্ধ (আমি বাইট কপি করতে সর্বোচ্চ সংখ্যক উল্লেখ পাবে প্রয়োজন) code.msdn .microsoft.com / অনুচ্ছেদ উদাহরণ
মাইকেল

1
FIY, অনুকূল বাফারের আকার গান iz 81920বাইট, না32768
অ্যালেক্স Zhukovskiy

2
@Jeff সর্বশেষ referecnceSource শো এটা আসলে ব্যবহার করে 81920 বাইট বাফার।
অ্যালেক্স ঝুকভস্কি

66

মেমরিস্ট্রিম রয়েছে। রাইটওটো (আউটস্ট্রিম);

এবং .NET 4.0 এর সাধারণ স্ট্রিম অবজেক্টে কপিটি আছে।

নেট 4.0:

instream.CopyTo(outstream);

এই পদ্ধতিগুলি ব্যবহার করে ওয়েবে অনেক নমুনা দেখতে পাচ্ছি না। এটি কি কারণ তারা মোটামুটি নতুন বা কিছু সীমাবদ্ধতা রয়েছে?
জিনস

3
এটি নেট নেট 4.0.০ এ নতুন কারণ। স্ট্রিম.কপিটি () মূলত অনুমোদিত উত্তরের সাথে লুপের জন্য ঠিক একই কাজ করে যা কিছু অতিরিক্ত স্যানিটি পরীক্ষা করে। ডিফল্ট বাফার আকার 4096, তবে একটি বৃহত্তর একটি নির্দিষ্ট করতে একটি ওভারলোডও রয়েছে।
মাইকেল ইডেনফিল্ড

9
স্ট্রিমটি অনুলিপি করার পরে পুনরায় রিয়েলাইন্ড করা দরকার: ইনস্ট্রিম.পজিশন = 0;
Draykos

6
ইনপুট স্ট্রিমটিকে রিন্ডাইন্ড করার পাশাপাশি, আমি আউটপুট স্ট্রিমটিকে রিন্ডাইন্ড করারও একটি প্রয়োজনীয়তা পেয়েছি: আউটস্ট्रीम P পজিশন = 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

"কপিরাইটস্ট্রিম" এর প্রয়োগগুলি পৃথক করে এমন প্রাথমিক প্রশ্নগুলি হ'ল:

  • পঠন বাফার আকার
  • লেখার আকার
  • আমরা কি একাধিক থ্রেড ব্যবহার করতে পারি (আমরা যখন পড়ছি তখন লেখা)।

এই প্রশ্নের উত্তরগুলি কপিরাইট স্ট্রিমের বিস্তৃতভাবে বিভিন্ন বাস্তবায়নের ফলাফল দেয় এবং আপনার কী ধরণের প্রবাহ রয়েছে এবং আপনি কী অনুকূলিত করার চেষ্টা করছেন তার উপর নির্ভরশীল। "সেরা" বাস্তবায়ন এমনকি স্ট্রিমগুলি কী নির্দিষ্ট হার্ডওয়্যারটি পড়ছে এবং কী লিখছিল তাও জানতে হবে।


1
... বা সেরা প্রয়োগের মাধ্যমে আপনাকে বাফারের আকার নির্দিষ্ট করতে, আকার লেখার জন্য এবং থ্রেডগুলি অনুমোদিত কিনা তা ওভারলোড হতে পারে?
মার্কজে

1

একটি স্ট্রিম অনুলিপি করার আসলে ভারী হাতে কম উপায় way তবে খেয়াল রাখুন, এর দ্বারা বোঝা যাচ্ছে যে আপনি পুরো ফাইলটি মেমরিতে সঞ্চয় করতে পারেন। আপনি যদি সাবধানতা ছাড়াই কয়েকশ মেগাবাইট বা তার বেশি সংখ্যক ফাইলগুলিতে চলে এমন ফাইলগুলির সাথে কাজ করে থাকেন তবে এটি ব্যবহার করে দেখুন না।

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 (ছাড়া একটি UTF8 হওয়া প্রবাহ সৃষ্টি msdn.microsoft.com/en-us/library/fysy0a4b.aspx ) তাই সমস্যার এনকোডিং কোন বিপদ আছে। বাইনারি ডেটা প্রায় অবশ্যই এইভাবে অনুলিপি করা উচিত নয়।
kͩeͣmͮpͥ ͩ

14
যে কেউ সহজেই তর্ক করতে পারে যে "মেমরির পুরো ফাইল" লোড করা খুব কমই "কম ভারী হাত" হিসাবে বিবেচিত হয়।
Seph

আমি এর কারণে প্রচলিত ব্যতিক্রম পাই
ColacX

এই না প্রবাহে প্রবাহ। reader.ReadToEnd()সবকিছু র‌্যামে রাখে
বিজান

1

। নেট ফ্রেমওয়ার্ক 4 সিস্টেমের স্ট্রিম ক্লাসের নতুন "কপিরাইটো" পদ্ধতিটি প্রবর্তন করে। এই পদ্ধতিটি ব্যবহার করে আমরা একটি স্ট্রিমকে বিভিন্ন স্ট্রিম শ্রেণির অন্য স্ট্রিমে অনুলিপি করতে পারি।

এখানে এর জন্য উদাহরণ।

    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
জেনেরিক সমাধানটি এখানে কাজ করবে - নিকের উত্তরটি ভাল a বাফার আকারটি অবশ্যই একটি স্বেচ্ছাসেবী পছন্দ, তবে 32 কে যুক্তিসঙ্গত মনে হয়। আমি মনে করি যে নিকের সমাধানটি স্ট্রিমগুলি বন্ধ না করে সঠিক is ​​এটি মালিককে ছেড়ে দিন।
জন স্কিটে

0

আপনি কী ধরণের স্ট্রিমের সাথে কাজ করছেন তার উপর নির্ভর করে আরও দক্ষতার সাথে এটি করার একটি উপায় থাকতে পারে। আপনি যদি আপনার দুটি বা দুটি স্ট্রিমকে মেমোরিস্ট্রিমে রূপান্তর করতে পারেন তবে আপনি আপনার ডেটা উপস্থাপন করে বাইট অ্যারে দিয়ে সরাসরি কাজ করতে গেটবফার পদ্ধতিটি ব্যবহার করতে পারেন। এটি আপনাকে অ্যারে.কপিটিওর মতো পদ্ধতি ব্যবহার করতে দেয় যা ফ্রাইগাইবব দ্বারা উত্থাপিত সমস্ত বিষয়কে অ্যাবস্ট্রাক্ট করে। ডেটা অনুলিপি করার সর্বোত্তম উপায়টি জানতে আপনি নেটকে বিশ্বাস করতে পারেন।


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
অবশ্যই যদি দ্বিতীয় পাঠটি প্রথম লেখার আগে সম্পূর্ণ হয় তবে আপনি লেখার আগেই প্রথম পঠন থেকে বাফারফোর্ডের বিষয়বস্তু লিখবেন it
পিটার জেফারি

0

.NET 3.5 এর জন্য এবং চেষ্টা করার আগে:

MemoryStream1.WriteTo(MemoryStream2);

এটি কেবল তখনই কাজ করে যদি আপনি মেমরিস্ট্রিমগুলি নিয়ে কাজ করছেন।
নাইয়ারগডস

0

সহজ এবং নিরাপদ - মূল উত্স থেকে নতুন স্ট্রিম তৈরি করুন:

    MemoryStream source = new MemoryStream(byteArray);
    MemoryStream copy = new MemoryStream(byteArray);
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.