سؤال

لدي خادم ويب يقوم بقراءة الملفات الثنائية الكبيرة (عدة ميغابايت) في صفائف البايت. يمكن أن يقرأ الخادم العديد من الملفات في نفس الوقت (طلبات صفحة مختلفة)، لذلك أبحث عن الطريقة الأكثر حساسة للقيام بذلك دون فرض ضرائب على وحدة المعالجة المركزية أكثر من اللازم. هل الكود أدناه جيد بما فيه الكفاية؟

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;
}
هل كانت مفيدة؟

المحلول

ببساطة استبدال كل شيء مع:

return File.ReadAllBytes(fileName);

ومع ذلك، إذا كنت تشعر بالقلق إزاء استهلاك الذاكرة، فيجب عليك ليس اقرأ الملف بالكامل في الذاكرة مرة واحدة على الإطلاق. يجب عليك القيام بذلك في قطع.

نصائح أخرى

قد أجادل أن الجواب هنا عموما هو "لا". إلا انت حاجة تماما جميع البيانات في وقت واحد، والنظر في استخدام Stream- API Based (أو بعض البديل من القارئ / اختبار ITERATOR). إنه خاصة هام عندما يكون لديك عمليات متوازية متعددة (كما اقترح السؤال) لتقليل تحميل النظام وزيادة الإنتاجية.

على سبيل المثال، إذا كنت تقوم بتشفق البيانات إلى المتصل:

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

أعتقد أن هذا:

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

يمكن أن يكون التعليمات البرمجية الخاصة بك في هذا (بدلا من 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 هي بحجم مخزن مؤقت.

أود أن أقترح أيضا القراءة تيار ملف و bufferedstream.

كما هو الحال دائما برنامج عينة بسيط إلى الملف الشخصي الذي هو الأسرع سيكون مفيدا.

أيضا الأجهزة الأساسية الخاصة بك سيكون لها تأثير كبير على الأداء. هل تستخدم محركات الأقراص الصلبة المستندة للخادم مع مخابئ كبيرة وبطاقة RAID مع ذاكرة التخزين المؤقت للذاكرة على متن الطائرة؟ أم أنك تستخدم محرك أقراص قياسي متصل بمنفذ IDE؟

اعتمادا على تواتر العمليات وحجم الملفات وعدد الملفات التي تنظر إليها، هناك مشاكل أداء أخرى في الاعتبار. شيء واحد يجب تذكره، هو أن كل صفائف من بايت الخاص بك سيتم إصدارها تحت رحمة جامع القمامة. إذا لم تكن مخزئا بأي من هذه البيانات، فيمكنك أن تنتهي بإنشاء الكثير من القمامة وأن تفقد معظم أدائك ٪ الوقت في GC. وبعد إذا كانت القطع أكبر من 85K، فسوف تتخصص إلى كومة الكائنات الكبرى الكبيرة (LOH) والتي ستتطلب مجموعة من جميع الأجيال لتحرير (هذا مكلف للغاية، وعلى خادم سيتوقف عن كل التنفيذ بينما يحدث ). بالإضافة إلى ذلك، إذا كان لديك الكثير من الكائنات على اللوحة، فيمكنك في نهاية المطاف بتجزئة LOH (لا يتم ضغط اللوحة أبدا) مما يؤدي إلى أداء ضعيف وخارج استثناءات الذاكرة. يمكنك إعادة تدوير العملية بمجرد أن تضغط على نقطة معينة، لكنني لا أعرف ما إذا كانت هذه أفضل الممارسات.

هذه النقطة هي، يجب أن تنظر في دورة حياة كاملة من التطبيق الخاص بك قبل أن تقرأ بالضرورة كل البايتات في الذاكرة أسرع طريقة ممكنة أو قد تتداول أداء قصير الأجل للأداء العام.

أريد أن أقول 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 مغلقة والتخلص منها.

في حالة وجود مع "ملف كبير" يعني خارج حد 4GB، ثم منطقتي الكود المكتوي التالي المناسب. المشكلة الرئيسية التي يجب ملاحظتها هي نوع البيانات الطويل المستخدم مع طريقة السعي. بقدر الإشارة إلى ما بعد 2 ^ 32 حدود البيانات. في هذا المثال، يتم معالجة التعليمات البرمجية أولا معالجة الملف الكبير في قطع 1GB، بعد معالجة قطع 1GB كاملة كبيرة، تتم معالجة البايتات اليسرى (<1GB). يمكنني استخدام هذا الرمز مع حساب CRC من الملفات خارج حجم 4GB. (استخدام https://crc32c.machinezoo.com/ لحساب CRC32C في هذا المثال)

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

استخدم فئة المخزن المؤقت في C # لتحسين الأداء. العازلة عبارة عن كتلة من البايتات في الذاكرة المستخدمة إلى بيانات ذاكرة التخزين المؤقت، وبالتالي تقليل عدد المكالمات إلى نظام التشغيل. المخازن المؤقتة تحسين الأداء القراءة والكتابة.

راجع ما يلي مثال على التعليمات البرمجية والتفسير الإضافي:http://msdn.microsoft.com/en-us/library/system.io.bufferedstream.aspx.

استخدم هذا:

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

أود أن أوصي بحاول Response.TransferFile() الطريقة ثم أ. Response.Flush() و Response.End() لخدمة ملفاتك الكبيرة.

إذا كنت تتعامل مع الملفات فوق 2 غيغابايت، فستجد أن الطرق المذكورة أعلاه تفشل.

من الأسهل بكثير تسليم التدفق إلى MD5. والسماح بذلك لقطعة ملفك لك:

private byte[] computeFileHash(string filename)
{
    MD5 md5 = MD5.Create();
    using (FileStream fs = new FileStream(filename, FileMode.Open))
    {
        byte[] hash = md5.ComputeHash(fs);
        return hash;
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top