Wednesday 10 August 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