একটি বড় ফাইলকে সি # তে বাইট অ্যারেতে পড়ার সর্বোত্তম উপায়?


391

আমার একটি ওয়েব সার্ভার রয়েছে যা বড় বাইনারি ফাইলগুলি (বেশ কয়েকটি মেগাবাইট) বাইট অ্যারেতে পড়বে। সার্ভার একই সাথে বেশ কয়েকটি ফাইল পড়তে পারে (বিভিন্ন পৃষ্ঠার অনুরোধগুলি), তাই আমি সিপিইউকে অতিরিক্ত পরিমাণে কর না দিয়ে এটি করার জন্য সবচেয়ে অনুকূলিত উপায়টি খুঁজছি। নীচের কোডটি কি যথেষ্ট ভাল?

public byte[] FileToByteArray(string fileName)
{
    byte[] buff = null;
    FileStream fs = new FileStream(fileName, 
                                   FileMode.Open, 
                                   FileAccess.Read);
    BinaryReader br = new BinaryReader(fs);
    long numBytes = new FileInfo(fileName).Length;
    buff = br.ReadBytes((int) numBytes);
    return buff;
}

60
আপনার উদাহরণ সংক্ষেপে বলা যেতে পারে byte[] buff = File.ReadAllBytes(fileName)
জেসি সি স্লিকার 21

3
এটি তৃতীয় পক্ষের ওয়েব সার্ভিস হওয়ার অর্থ কেন ওয়েব স্ট্রিম না করে ওয়েব সার্ভিসে প্রেরণ করার আগে ফাইলটি সম্পূর্ণ র‌্যামে থাকা দরকার? ওয়েব সার্ভিস পার্থক্যটি জানতে পারবে না।
ব্রায়ান

@ ব্রায়ান, কিছু ক্লায়েন্ট উদাহরণস্বরূপ জাভার মতো একটি .NET স্ট্রিম কীভাবে পরিচালনা করবেন তা জানেন না। যখন এটি হয় তখন সমস্ত ফাইল বাইট অ্যারেতে পড়তে হয়।
শেজেফ্রে

4
@ সিজেফ্রে: আমি বলেছিলাম ডেটা প্রবাহিত করা উচিত, নেট স্ট্রিম হিসাবে পাস করা উচিত নয়। ক্লায়েন্টরা কোনওভাবেই পার্থক্যটি জানতে পারবে না।
ব্রায়ান

উত্তর:


776

কেবলমাত্র পুরো জিনিসটি এর সাথে প্রতিস্থাপন করুন:

return File.ReadAllBytes(fileName);

তবে, আপনি যদি মেমরির ব্যবহার সম্পর্কে উদ্বিগ্ন হন তবে আপনার পুরো ফাইলটি একবারে মেমরির মধ্যে একবারে পড়া উচিত নয় । আপনি খণ্ড খণ্ডে এটি করা উচিত।


40
এই পদ্ধতিটি 2 ^ 32 বাইট ফাইলের মধ্যে সীমাবদ্ধ (4.2 গিগাবাইট)
মাহমুদ ফারাহাত

11
ফাইল.আরেডলাইটস বড় ফাইলগুলির সাথে আউট অফ মেমরিএক্সেপশন ছুড়ে ফেলে (6৩০ এমবি ফাইল দিয়ে পরীক্ষা করা হয়েছে এবং এটি ব্যর্থ হয়েছে)
সেকিটো

6
@ জুয়ানজো.আরানা হ্যাঁ, ভাল ... অবশ্যই সবসময় এমন কিছু থাকবে যা মেমরির সাথে খাপ খায় না, এক্ষেত্রে প্রশ্নের উত্তর নেই। সাধারণত, আপনার ফাইলটি স্ট্রিম করা উচিত এবং এটিকে পুরোপুরি স্মৃতিতে সঞ্চয় করা উচিত নয়। আপনি স্টপগ্যাপ পরিমাপের জন্য এটি দেখতে চাইতে পারেন: এমএসডিএন.মাইক্রোসফটকম /en-us/library/hh285054%28v=vs.110%29.aspx
মেহরদাদ আফশারী

4
.NET এ অ্যারের আকারের সীমা রয়েছে, তবে .NET 4.5 এ আপনি বিশেষ কনফিগারেশন বিকল্প ব্যবহার করে বড় অ্যারে (> 2 গিগাবাইট) এর জন্য সমর্থন চালু করতে পারেন দেখুন msdn.microsoft.com/en-us/library/hh285054.aspx
অবৈধ -প্রবাসী

3
@ বড়গ না, এবং প্রশ্নটি এটিই বলে না।
মেহরদাদ আফশারি

72

আমি যুক্তি দিতে পারি যে এখানে উত্তরটি সাধারণত "না" হয়। আপনার একসাথে সমস্ত ডেটা প্রয়োজন না হলে একটি Streamভিত্তিক এপিআই (বা পাঠক / পুনরুক্তির কিছু বৈকল্পিক) ব্যবহার বিবেচনা করুন। এটি বিশেষত গুরুত্বপূর্ণ যখন আপনার সিস্টেমে লোড হ্রাস করতে এবং আউটপুট সর্বাধিকীকরণের জন্য একাধিক সমান্তরাল ক্রিয়াকলাপ (প্রশ্ন দ্বারা প্রস্তাবিত) হয়।

উদাহরণস্বরূপ, আপনি যদি একজন কলারে ডেটা স্ট্রিম করছেন:

Stream dest = ...
using(Stream source = File.OpenRead(path)) {
    byte[] buffer = new byte[2048];
    int bytesRead;
    while((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) {
        dest.Write(buffer, 0, bytesRead);
    }
}

3
আপনার বিবৃতিতে যুক্ত করতে, আমি এমনকি আপনার কাছে ক্লায়েন্টের কাছে কোনও ফাইল স্ট্রিম করার মতো আই / ও বাউন্ড অপারেশন থাকলে অ্যাসিঙ্ক এএসপি.নেট হ্যান্ডলারগুলি বিবেচনা করার পরামর্শ দিই। যাইহোক, আপনি যদি আছে একটি পুরো ফাইলটি পড়ার byte[]কোনো কারণে, আমি স্ট্রিম বা অন্য কিছু ব্যবহার করবেন না সুপারিশ এবং মাত্র এপিআই প্রদান সিস্টেমটি ব্যবহার করুন।
মেহরদাদ আফশারি

@ মেহরদাদ - সম্মত; তবে পুরো প্রসঙ্গটি পরিষ্কার নয়। তেমনি এমভিসিরও এর জন্য অ্যাকশন-ফলাফল রয়েছে।
মার্ক Gravell

হ্যাঁ আমার একবারে সমস্ত ডেটা দরকার। এটি একটি তৃতীয় পক্ষের ওয়েব সার্ভিসে যাচ্ছে।
টনি_হেনরিচ

সিস্টেম সরবরাহিত এপিআই কি?
টনি_হেনরিচ

1
@Tony: আমি কি আমার উত্তরে বলেন: File.ReadAllBytes
মেহরদাদ আফশারি

32

আমি এটি ভাবব:

byte[] file = System.IO.File.ReadAllBytes(fileName);

3
মনে রাখবেন যে সত্যই বড় ফাইলগুলি পাওয়ার সময় এটি স্টল করতে পারে।
vapcguy

28

আপনার কোডটি এটিকে ফ্যাক্টর করা যায় (ফাইলের পরিবর্তে eআল্ডবাইটস):

public byte[] ReadAllBytes(string fileName)
{
    byte[] buffer = null;
    using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
    {
        buffer = new byte[fs.Length];
        fs.Read(buffer, 0, (int)fs.Length);
    }
    return buffer;
} 

Integer.MaxValue - ফাইলের আকারের সীমাবদ্ধতা পড়ুন পদ্ধতির দ্বারা নোট করুন। অন্য কথায় আপনি একবারে কেবল 2 জিবি অংশটি পড়তে পারেন।

আরও মনে রাখবেন যে ফাইল স্ট্রিমের সর্বশেষ যুক্তিটি একটি বাফার আকার।

আমিও সম্পর্কে পড়া সুপারিশ করবে FileStream এবং BufferedStream

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

এছাড়াও আপনার অন্তর্নিহিত হার্ডওয়্যার কর্মক্ষমতা উপর একটি বড় প্রভাব ফেলবে। আপনি কি বড় ক্যাশে সহ সার্ভার ভিত্তিক হার্ড ডিস্ক ড্রাইভ এবং অনবোর্ড মেমোরি ক্যাশে সহ একটি রেড কার্ড ব্যবহার করছেন? বা আপনি আইডিই পোর্টের সাথে সংযুক্ত একটি স্ট্যান্ডার্ড ড্রাইভ ব্যবহার করছেন?


কেন হার্ডওয়্যার ধরণের একটি পার্থক্য করতে হবে? সুতরাং এটির আইডি হলে আপনি কিছু। নেট পদ্ধতি ব্যবহার করেন এবং এটি যদি রেড হয় তবে আপনি অন্যটি ব্যবহার করেন?
টনি_হেনরিচ

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

2
এই কোডটিতে একটি জটিল ত্রুটি রয়েছে। পড়ার জন্য কমপক্ষে 1 বাইট ফেরত আসতে হবে।
মাফু

আমি নিশ্চিত হয়েছি যে এইভাবে চেক করা কন্সট্রাক্টটি দিয়ে দীর্ঘ টু কাস্ট কাস্ট করা আবশ্যক: চেক করা ((অন্তর্) fs.Length)
tzup

আমি ঠিক var binaryReader = new BinaryReader(fs); fileData = binaryReader.ReadBytes((int)fs.Length);যে usingবিবৃতি করতে হবে। কিন্তু যে কি ওপি করেনি, শুধু আমি কোড একটি লাইন কাস্ট করে কাটা মত কার্যকরভাবে fs.Lengthকরতে intপরিবর্তে পেয়ে longমান FileInfoদৈর্ঘ্য এবং যে রূপান্তর।
vapcguy

9

অপারেশনগুলির ফ্রিকোয়েন্সি, ফাইলগুলির আকার এবং আপনি যে ফাইলগুলির সন্ধান করছেন তার উপর নির্ভর করে অন্যান্য কার্যকারিতা বিবেচনায় নেওয়া উচিত। একটি জিনিস মনে রাখবেন, তা হচ্ছে আপনার প্রতিটি বাইট অ্যারে আবর্জনা সংগ্রাহকের করুণায় প্রকাশ করা হবে। যদি আপনি সেই ডেটাগুলির কোনওটিই ক্যাচ না করে থাকেন তবে আপনি প্রচুর আবর্জনা তৈরি করতে পারেন এবং আপনার বেশিরভাগ পারফরম্যান্সকে জি.সি.- তে % সময়কে হারিয়ে ফেলতে পারেন। যদি খণ্ডগুলি 85 কে এর চেয়ে বড় হয় তবে আপনাকে বড় অবজেক্ট হিপ (এলওএইচ) এ বরাদ্দ দেওয়া হবে যার জন্য সমস্ত প্রজন্মের মুক্ত হওয়া প্রয়োজন (এটি খুব ব্যয়বহুল, এবং কোনও সার্ভারে চলতে চলাকালীন সমস্ত সম্পাদন বন্ধ করে দেবে) )। অতিরিক্তভাবে, যদি আপনার এলওএইচ-তে এক টন অবজেক্ট থাকে তবে আপনি LOH টুকরো টুকরো টুকরো করে শেষ করতে পারেন (এলওএইচ কখনই সংক্ষিপ্ত হয় না) যা খারাপ পারফরম্যান্স এবং স্মৃতি ব্যাতিক্রমের দিকে পরিচালিত করে। আপনি নির্দিষ্ট পয়েন্টটি একবার আঘাত করার পরে আপনি প্রক্রিয়াটি পুনর্ব্যবহার করতে পারেন, তবে এটি সেরা অভ্যাস কিনা তা আমি জানি না।

মুল বক্তব্যটি হ'ল আপনার অ্যাপ্লিকেশনটির পুরো জীবনচক্রটি অবশ্যই বিবেচনা করা উচিত অগত্যা সমস্ত গতিতে বাইটগুলি মেমোরিতে দ্রুততমভাবে পড়া সম্ভব অথবা আপনি সামগ্রিক পারফরম্যান্সের জন্য স্বল্পমেয়াদী পারফরম্যান্স ট্রেড করতে পারেন।


সোর্স কোড সি এটি সম্পর্কে #, জন্য পরিচালনা garbage collector, chunks, কর্মক্ষমতা, ঘটনা কাউন্টারে , ...
PreguntonCojoneroCabrón

6

আমি বলছি BinaryReaderঠিক আছে, তবে বাফারের দৈর্ঘ্য পাওয়ার জন্য কোডের সমস্ত লাইন পরিবর্তে এটির সাথে পুনঃঅ্যাক্টর করা যেতে পারে:

public byte[] FileToByteArray(string fileName)
{
    byte[] fileData = null;

    using (FileStream fs = File.OpenRead(fileName)) 
    { 
        using (BinaryReader binaryReader = new BinaryReader(fs))
        {
            fileData = binaryReader.ReadBytes((int)fs.Length); 
        }
    }
    return fileData;
}

ব্যবহারের চেয়ে ভাল হওয়া উচিত .ReadAllBytes(), যেহেতু আমি শীর্ষ প্রতিক্রিয়াতে মন্তব্যগুলিতে দেখেছি যার মধ্যে রয়েছে .ReadAllBytes()যে মন্তব্যকারীদের মধ্যে একটিতে> 600 এমবি ফাইল নিয়ে সমস্যা ছিল, যেহেতু একটি BinaryReaderএই ধরণের জিনিসটির জন্য বোঝানো হয়। এছাড়াও, এটি একটি usingবিবৃতিতে রাখা নিশ্চিত করে FileStreamএবং BinaryReaderএটি বন্ধ এবং নিষ্পত্তি হয়।


সি # এর জন্য উপরে বর্ণিত হিসাবে "ব্যবহার করে (ফাইলস্ট্রিম fs = নতুন ফাইল.অপেনআরইড (ফাইলনাম))" এর পরিবর্তে "(ফাইলস্ট্রিম fs = ফাইল.অপেনআরইড (ফাইলের নাম))" ব্যবহার করা দরকার। ফাইলের আগে সবেমাত্র নতুন কীওয়ার্ড সরানো হয়েছে .অপনারড ()
সৈয়দ মোহাম্মদ

@ সাইয়েড উপরের কোডটি সি # এর জন্য লেখা ছিল, তবে আপনি ঠিক বলেছেন newযে সেখানে প্রয়োজনীয় ছিল না। সরানো হয়েছে।
vapcguy

1

যদি 'একটি বৃহত ফাইল' 4GB সীমা ছাড়িয়ে বোঝানো হয় তবে আমার নিম্নলিখিত লিখিত কোড যুক্তিটি উপযুক্ত। লক্ষ্য করার মূল বিষয়টি হ'ল SEEK পদ্ধতিতে ব্যবহৃত দীর্ঘ তথ্য প্রকার। একটি দীর্ঘ হিসাবে 2 ONG 32 ডেটা সীমানা অতিক্রম করতে সক্ষম। এই উদাহরণস্বরূপ, কোডটি প্রথমে 1GB এর অংশগুলিতে বৃহত ফাইলটি প্রক্রিয়াজাতকরণ করছে, বৃহত পুরো 1GB অংশগুলি প্রক্রিয়া করার পরে, বাম ওভার (<1 জিবি) বাইটগুলি প্রক্রিয়া করা হয়। আমি 4 জিবি আকারের বাইরে ফাইলগুলির সিআরসি গণনা করে এই কোডটি ব্যবহার করি। ( এই উদাহরণে crc32c গণনার জন্য https://crc32c.machinezoo.com/ ব্যবহার করে)

private uint Crc32CAlgorithmBigCrc(string fileName)
{
    uint hash = 0;
    byte[] buffer = null;
    FileInfo fileInfo = new FileInfo(fileName);
    long fileLength = fileInfo.Length;
    int blockSize = 1024000000;
    decimal div = fileLength / blockSize;
    int blocks = (int)Math.Floor(div);
    int restBytes = (int)(fileLength - (blocks * blockSize));
    long offsetFile = 0;
    uint interHash = 0;
    Crc32CAlgorithm Crc32CAlgorithm = new Crc32CAlgorithm();
    bool firstBlock = true;
    using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
    {
        buffer = new byte[blockSize];
        using (BinaryReader br = new BinaryReader(fs))
        {
            while (blocks > 0)
            {
                blocks -= 1;
                fs.Seek(offsetFile, SeekOrigin.Begin);
                buffer = br.ReadBytes(blockSize);
                if (firstBlock)
                {
                    firstBlock = false;
                    interHash = Crc32CAlgorithm.Compute(buffer);
                    hash = interHash;
                }
                else
                {
                    hash = Crc32CAlgorithm.Append(interHash, buffer);
                }
                offsetFile += blockSize;
            }
            if (restBytes > 0)
            {
                Array.Resize(ref buffer, restBytes);
                fs.Seek(offsetFile, SeekOrigin.Begin);
                buffer = br.ReadBytes(restBytes);
                hash = Crc32CAlgorithm.Append(interHash, buffer);
            }
            buffer = null;
        }
    }
    //MessageBox.Show(hash.ToString());
    //MessageBox.Show(hash.ToString("X"));
    return hash;
}

0

পারফরম্যান্স উন্নত করতে সি # তে বাফারডস্ট্রিম ক্লাসটি ব্যবহার করুন। একটি বাফার ডেটা ক্যাশে করার জন্য মেমোরিতে ব্যবহৃত বাইটের একটি ব্লক, যার ফলে অপারেটিং সিস্টেমে কলগুলির সংখ্যা হ্রাস পায়। বাফারগুলি পড়ার এবং লেখার কর্মক্ষমতা উন্নত করে।

একটি কোড উদাহরণ এবং অতিরিক্ত ব্যাখ্যা জন্য নিম্নলিখিত দেখুন: http://msdn.microsoft.com/en-us/library/system.io.bufferedstream.aspx


আপনি BufferedStreamযখন পুরো জিনিসটি একবারে পড়ছেন তখন এমনটি ব্যবহার করার কী দরকার?
মেহেরদাদ আফশারি

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

9
কোনও অপারেশনের প্রসঙ্গে পারফরম্যান্স পরিমাপযোগ্য। স্ট্রিমের জন্য অতিরিক্ত বাফারিং যা আপনি ক্রমানুসারে পড়ছেন, সমস্ত একবারে, মেমরির কোনও অতিরিক্ত বাফার থেকে উপকৃত হওয়ার সম্ভাবনা নেই।
মেহরদাদ আফশারি

0

এটা ব্যবহার কর:

 bytesRead = responseStream.ReadAsync(buffer, 0, Length).Result;

2
স্ট্যাক ওভারফ্লোতে স্বাগতম! ব্যাখ্যা যেমন এই প্ল্যাটফর্মের উত্তরের একটি গুরুত্বপূর্ণ অঙ্গ, দয়া করে আপনার কোডটি ব্যাখ্যা করুন এবং এটি কীভাবে সমস্যার সমাধান করে এবং অন্যান্য উত্তরের চেয়ে কেন এটি আরও ভাল হতে পারে তা ব্যাখ্যা করুন। আমাদের গাইড কীভাবে একটি ভাল উত্তর লিখবেন তা আপনার পক্ষে সহায়ক হতে পারে। ধন্যবাদ
ডেভিড

-4

আমি Response.TransferFile()পদ্ধতিটি চেষ্টা করার পরামর্শ দিচ্ছি তখন একটি Response.Flush()এবং Response.End()আপনার বড় ফাইলগুলি পরিবেশন করার জন্য।


-7

যদি আপনি 2 জিবি-র উপরে ফাইলগুলি নিয়ে কাজ করে থাকেন তবে আপনি দেখতে পাবেন যে উপরের পদ্ধতিগুলি ব্যর্থ।

এমডি 5 এর কাছে স্রোতটি বন্ধ করা এবং এটি আপনার জন্য আপনার ফাইলটিকে ছাঁটাইতে দেওয়া অনেক সহজ :

private byte[] computeFileHash(string filename)
{
    MD5 md5 = MD5.Create();
    using (FileStream fs = new FileStream(filename, FileMode.Open))
    {
        byte[] hash = md5.ComputeHash(fs);
        return hash;
    }
}

11
কোডটি প্রশ্নের সাথে কীভাবে প্রাসঙ্গিক তা আমি দেখতে পাচ্ছি না (বা আপনি লিখিত পাঠ্যে কী পরামর্শ দিচ্ছেন)
ভোজটেক বি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.