Wednesday, August 10, 2011

Хеш код для папки (MD5).

Сьогодні я хочу написати дещо про задачу з якою стикнувся на роботі. Суть її в тому що мені треба промоніторити чи змінились файли в конкретній папці і при наявності будь яких змін вивести повідомлення і тд. Відслідковувати необхідно сам факт зміни даних в каталозі отож логічно було зроблено припущення що по папці нам необхідно генерувати якийсь хешкод і порівнювати його при заупуску програми.

Для діставання хешу файлу досить скоро було знайдено рішення, але нічого схожого бля папки не було…





try

{

    FileStream file = new FileStream(fileName, FileMode.Open);

    MD5 md5 = new MD5CryptoServiceProvider();

    byte[] retVal = md5.ComputeHash(file);

    file.Close();

 

    StringBuilder sb = new StringBuilder();

    for (int i = 0; i < retVal.Length; i++)

    {

        sb.Append(retVal[i].ToString("x2"));

    }

    return sb.ToString();

}

catch (FileNotFoundException e)

{

    Log.Debug(e.Message);

}





Отож якщо в нас в папці 100 файлів ми отримуємо 100 хешів і їхнє зберігання та порівняння не є приємлимим рішенням. Після недовгих роздумів було знайдено просте як дошка рішення яким і хочу з вами поділитись, суть його заключається в тому що ми можемо взяти хеш або для стріма або для масиву байт, в даному випадку ми беремо всі хеші для файлів і обєднуємо їх, після чого цю стрічку перетворюємо в масив байтів і генеруємо для неї свій хеш. Ось як виглядає код:



public string GetMD5ForFolder(IEnumerable<FileInfo> listOfFiles)

       {

           string result = string.Empty;

           try

           {

               foreach (FileInfo file in listOfFiles)

               {

                   result = result + GetMD5HashFromFile(file.FullName);

               }

 

               MD5 md5 = new MD5CryptoServiceProvider();

               byte[] computeHash = md5.ComputeHash(Encoding.Default.GetBytes(result));

 

               StringBuilder sBuilder = new StringBuilder();

               for (int i = 0; i < computeHash.Length; i++)

               {

                   sBuilder.Append(computeHash[i].ToString("x2"));

               }

               return sBuilder.ToString();

           }

           catch (ArgumentNullException ex)

           {

               Log.Error(ex.Message);

               throw new ServiceAgentsException(ex.Message);

           }           

       }



Я не тестував це рішення на великих каталогах, але для каталогу з кількістью файлів до 100 я думаю що воно працюватиме без проблем. Як завжди буду радий почути будь які коменти що до коду і побачити кращі варіанти реалізації цієї задачі.

2 comments:

  1. Можна спростити всю роботу до використання класу FileSystemWatcher і підписатися на події Created, Changed, Renamed, Deleted і т.д. Також можна відслідковувати і для підкаталогів (властивість IncludeSubdirectories)

    ReplyDelete
  2. Привіт, для того щоб твій метод працював необхідно щоб програма була завжди ввімкненою, а метод який я описав вище, створений для того, щоб перевіряти каталоги на наявність змін між запусками програми.

    Практично в любий момент часу ми можемо впевнитись в тому що з моменту останнього запуску нічого не змінилось і ми можемо продовжити.

    ПС. про FileSystemWatcher я писав раніше http://batsihor.blogspot.com/2010/07/filesystemwatcher.html

    ReplyDelete