C # में बाइट सरणी में एक बड़ी फ़ाइल को पढ़ने का सबसे अच्छा तरीका?


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)
जेसी सी। स्लीकर

3
यह थर्ड पार्टी वेब्स सर्विस क्यों है, इसका मतलब यह है कि फाइल को पूरी तरह से रैम में रखने की जरूरत है, जिसे स्ट्रीम करने के बजाय वेब्स सर्विस में भेजा जाए? Webservice को अंतर नहीं पता होगा।
ब्रायन

@ ब्रायन, कुछ क्लाइंट्स को पता नहीं है कि उदाहरण के लिए जावा जैसी .NET स्ट्रीम को कैसे हैंडल किया जाए। जब यह मामला हो सकता है तो बाइट ऐरे में पूरी फाइल को पढ़ना है।
सजेफ्रे

4
@sjeffrey: मैंने कहा कि डेटा को स्ट्रीम किया जाना चाहिए, .NET स्ट्रीम के रूप में पास नहीं किया जाना चाहिए। ग्राहकों को इस तरह से अंतर नहीं पता चलेगा।
ब्रायन

जवाबों:


776

बस पूरी चीज़ को इसके साथ बदलें:

return File.ReadAllBytes(fileName);

हालाँकि, यदि आप मेमोरी खपत के बारे में चिंतित हैं, तो आपको एक बार में पूरी फ़ाइल को मेमोरी में नहीं पढ़ना चाहिए । आपको ऐसा करना चाहिए।


40
यह विधि 2 ^ 32 बाइट फ़ाइलों (4.2 जीबी) तक सीमित है
महमूद फ़रहत

11
File.ReadAllBytes बड़ी फ़ाइलों के साथ OutOfMemoryException फेंकता है (630 MB फ़ाइल के साथ परीक्षण किया गया और यह विफल रहा)
sakito

6
@ juanjo.arana हाँ, ठीक है ... निश्चित रूप से हमेशा कुछ ऐसा होगा जो स्मृति में फिट नहीं होता है, इस मामले में, सवाल का कोई जवाब नहीं है। आम तौर पर, आपको फ़ाइल को स्ट्रीम करना चाहिए और इसे मेमोरी में पूरी तरह से स्टोर नहीं करना चाहिए। : आप एक कामचलाऊ उपाय के लिए इस को देखने के लिए चाहते हो सकता है msdn.microsoft.com/en-us/library/hh285054%28v=vs.110%29.aspx
Mehrdad Afshari

4
.NET में सरणी आकार के लिए एक सीमा है, लेकिन .NET 4.5 में आप बड़े कॉन्फ़िगरेशन (> 2GB) के लिए समर्थन को चालू कर सकते हैं विशेष कॉन्फ़िगरेशन विकल्प का उपयोग करके देखें msdn.microsoft.com/en-us/library/hh285054.aspx
अवैध -ग्रिमेंट

3
@harag नहीं, और यह वह नहीं है जो सवाल पूछता है।
मेहरदाद अफश्री

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
आपके कथन में जोड़ने के लिए, मैं भी async ASP.NET हैंडलर पर विचार करने का सुझाव देता हूं यदि आपके पास क्लाइंट के लिए एक फ़ाइल को स्ट्रीम करने जैसा आई / ओ बाध्य ऑपरेशन है। हालांकि, अगर आपको किसी कारण से पूरी फाइल को पढ़ना है byte[], तो मेरा सुझाव है कि धाराओं या किसी अन्य चीज का उपयोग करने से बचें और बस उपलब्ध कराई गई प्रणाली का उपयोग करें।
मेहरदाद आफश्री

@ मेहरदाद - सहमत; लेकिन पूरा संदर्भ स्पष्ट नहीं है। इसी तरह MVC के पास इसके लिए कार्रवाई-परिणाम हैं।
मार्क Gravell

हां मुझे एक बार में सभी डेटा चाहिए। यह एक तृतीय पक्ष की वेब सेवा पर जा रहा है।
टोनी_ हेनरिक

एपीआई प्रदान किया गया सिस्टम क्या है?
टोनी_ हेनरिक

1
@ टिप्पणी: मैंने अपने उत्तर में कहा File.ReadAllBytes:।
मेहरदाद अफश्री

32

मुझे लगता है कि यह होगा:

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

3
ध्यान दें कि यह वास्तव में बड़ी फ़ाइलों को प्राप्त करने पर स्टाल कर सकता है।
vapcguy

28

आपके कोड को इसके बारे में बताया जा सकता है (File.ReadAllBytes के बदले में):

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 पर ध्यान दें - फ़ाइल आकार सीमा पढ़ें विधि द्वारा रखी गई है। दूसरे शब्दों में, आप केवल एक बार में 2GB का हिस्सा पढ़ सकते हैं।

यह भी ध्यान दें कि FileStream का अंतिम तर्क एक बफर आकार है।

मैं FileStream और BufferedStream के बारे में पढ़ने का सुझाव दूंगा

प्रोफ़ाइल के लिए हमेशा एक सरल नमूना कार्यक्रम जो सबसे तेज़ है, सबसे अधिक फायदेमंद होगा।

साथ ही आपके अंतर्निहित हार्डवेयर का प्रदर्शन पर बड़ा प्रभाव पड़ेगा। क्या आप बड़े कैश के साथ सर्वर आधारित हार्ड डिस्क ड्राइव और ऑनबोर्ड मेमोरी कैश के साथ एक RAID कार्ड का उपयोग कर रहे हैं? या क्या आप आईडीई पोर्ट से जुड़े एक मानक ड्राइव का उपयोग कर रहे हैं?


हार्डवेयर के प्रकार में अंतर क्यों आएगा? तो अगर यह आईडीई है तो आप कुछ .NET पद्धति का उपयोग करते हैं और यदि यह RAID है तो आप दूसरे का उपयोग करते हैं?
टोनी_ हेनरिक

@Tony_Henrich - इसका आपके प्रोग्रामिंग भाषा से कॉल करने से कोई लेना-देना नहीं है। विभिन्न प्रकार के हार्ड डिस्क ड्राइव हैं। उदाहरण के लिए, सीगेट ड्राइव को "एएस" या "एनएस" के रूप में वर्गीकृत किया जाता है, जिसमें एनएस सर्वर आधारित होता है, बड़ी कैश ड्राइव होती है, जहां "एएस" ड्राइव उपभोक्ता के रूप में होती है - होम कंप्यूटर आधारित ड्राइव। गति और आंतरिक स्थानांतरण दर भी प्रभावित करती हैं कि आप कितनी तेजी से डिस्क से कुछ पढ़ सकते हैं। RAID सरणियों को कैशिंग के माध्यम से पढ़ने / लिखने के प्रदर्शन में काफी सुधार किया जा सकता है। तो आप फ़ाइल को एक ही बार में पढ़ सकते हैं, लेकिन अंतर्निहित हार्डवेयर अभी भी निर्णायक कारक है।

2
इस कोड में एक महत्वपूर्ण बग है। केवल कम से कम 1 बाइट वापस करने के लिए आवश्यक है पढ़ें।
मफू

मैं इस तरह के चेक किए गए निर्माण के साथ इंट कास्ट को लंबे समय तक लपेटना सुनिश्चित करूंगा: चेक किया गया ((int) fs.Length)
tzup

मैं बस var binaryReader = new BinaryReader(fs); fileData = binaryReader.ReadBytes((int)fs.Length);उस usingबयान में करूँगा । लेकिन ओपी ने जो किया, वह प्रभावी है, बस मैंने लंबाई का मान प्राप्त करने और उसे परिवर्तित fs.Lengthकरने के intबजाय कास्टिंग द्वारा कोड की एक पंक्ति काट दी । longFileInfo
vapcguy

9

संचालन की आवृत्ति के आधार पर, फ़ाइलों का आकार, और उन फ़ाइलों की संख्या, जिन्हें आप देख रहे हैं, ध्यान देने के लिए अन्य प्रदर्शन मुद्दे हैं। एक बात याद रखें, यह है कि आपके प्रत्येक बाइट सरणियों को कचरा कलेक्टर की दया पर जारी किया जाएगा। यदि आप उस किसी भी डेटा को कैशिंग नहीं कर रहे हैं, तो आप बहुत सारे कचरे का निर्माण कर सकते हैं और अपने अधिकांश प्रदर्शन को जीसी में%% तक खो सकते हैं।। यदि विखंड 85K से बड़े हैं, तो आप लार्ज ऑब्जेक्ट हीप (LOH) को आवंटित करेंगे, जिसे खाली करने के लिए सभी पीढ़ियों के संग्रह की आवश्यकता होगी (यह बहुत महंगा है, और सर्वर पर सभी निष्पादन बंद हो जाएगा, जबकि यह चल रहा है )। इसके अतिरिक्त, यदि आपके पास LOH पर एक टन की वस्तु है, तो आप LOH विखंडन (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()यह भी शामिल है कि टिप्पणीकारों में से एक BinaryReaderको फ़ाइलों के साथ समस्या थी> 600 एमबी, क्योंकि इस तरह की चीज़ के लिए इसका मतलब है। इसके अलावा, इसे एक usingबयान में रखना सुनिश्चित करता है FileStreamऔर BinaryReaderबंद और निपटारा किया जाता है।


C # के लिए, ऊपर दिए गए अनुसार ("FileStream fs = new File.OpenRead (fileName)" का उपयोग करने के बजाय "FileStream fs = File.OpenRead (fileName)" का उपयोग करने की आवश्यकता है। File.OpenRead ()
सैयद मोहम्मद

@Syed WAS के ऊपर का कोड C # के लिए लिखा newगया था , लेकिन आप सही हैं कि इसकी आवश्यकता नहीं थी। हटा दिया।
vapcguy

1

'बड़ी फाइल' के मामले में 4GB की सीमा से परे है, तो मेरा निम्नलिखित लिखित तर्क उचित है। नोटिस करने के लिए महत्वपूर्ण मुद्दा SEEK विधि के साथ इस्तेमाल किया गया LONG डेटा प्रकार है। एक लंबी के रूप में 2 ^ 32 डेटा सीमाओं से परे इंगित करने में सक्षम है। इस उदाहरण में, कोड पहली फ़ाइल को बड़ी मात्रा में 1GB के प्रसंस्करण में संसाधित कर रहा है, बड़े पूरे 1GB के टुकड़े को संसाधित करने के बाद, बाईं ओर (<1GB) बाइट्स संसाधित किए जाते हैं। मैं इस कोड का उपयोग 4GB आकार से परे फाइलों की सीआरसी की गणना के साथ करता हूं। ( इस उदाहरण में 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

प्रदर्शन को बेहतर बनाने के लिए C # में बफ़रडस्ट्रीम क्लास का उपयोग करें। एक बफर डेटा को कैश करने के लिए उपयोग की जाने वाली मेमोरी में बाइट्स का एक ब्लॉक है, जिससे ऑपरेटिंग सिस्टम पर कॉल की संख्या कम हो जाती है। बफ़र पढ़ने और प्रदर्शन को बेहतर बनाते हैं।

एक कोड उदाहरण और अतिरिक्त स्पष्टीकरण के लिए निम्नलिखित देखें: 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
मैं यह नहीं देखता कि प्रश्न के लिए कोड कैसे प्रासंगिक है (या आप लिखित पाठ में क्या सुझाते हैं)
Vojtech B
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.