Почему эти два файла имеют одно и то же значение, когда я использую MemoryStream?

Я пишу подпрограмму С#, которая создает хэши из файлов jpg. Если я передам массив байтов моему объекту SHA512, я получу ожидаемое поведение, однако, если я передам поток памяти, два файла всегда будут иметь одно и то же значение.

Пример 1:

        SHA512 mySHA512 = SHA512.Create();

        Image img1 = Image.FromFile(@"d:\img1.jpg");
        Image img2 = Image.FromFile(@"d:\img2.jpg");
        MemoryStream ms1 = new MemoryStream();
        MemoryStream ms2 = new MemoryStream();

        img1.Save(ms1, ImageFormat.Jpeg);
        byte[] buf1 = ms1.GetBuffer();
        byte[] hash1 = mySHA512.ComputeHash(buf1);

        img2.Save(ms2, ImageFormat.Jpeg);
        byte[] buf2 = ms2.GetBuffer();
        byte[] hash2 = mySHA512.ComputeHash(buf2);

        if (Convert.ToBase64String(hash1) == Convert.ToBase64String(hash2))
            MessageBox.Show("Hashed the same");
        else
            MessageBox.Show("Different hashes");

Это дает «Разные хэши». Но одна из перегрузок метода ComputeHash принимает объект потока, и я бы предпочел использовать ее. Когда я делаю:

        SHA512 mySHA512 = SHA512.Create();

        Image img1 = Image.FromFile(@"d:\img1.jpg");
        Image img2 = Image.FromFile(@"d:\img2.jpg");
        MemoryStream ms1 = new MemoryStream();
        MemoryStream ms2 = new MemoryStream();

        img1.Save(ms1, ImageFormat.Jpeg);
        byte[] hash1 = mySHA512.ComputeHash(ms1);

        img2.Save(ms2, ImageFormat.Jpeg);
        byte[] hash2 = mySHA512.ComputeHash(ms2);

        if (Convert.ToBase64String(hash1) == Convert.ToBase64String(hash2))
            MessageBox.Show("Hashed the same");
        else
            MessageBox.Show("Different hashes");

Это дает «Хешированное то же самое».

Что здесь происходит, чего мне не хватает?


person Lee Warner    schedule 11.11.2009    source источник
comment
Это не копии одного и того же изображения, не так ли?   -  person Joel Coehoorn    schedule 11.11.2009
comment
Что произойдет, если вы перейдете к началу потока после вызова .Save? например ms1.Seek(0, SeekOrigin.Begin);   -  person Joe    schedule 11.11.2009
comment
В этом случае вы можете использовать mySHA512.ComputeHash.ComputeHash(ms1.GetBuffer(), 0, (int)ms1.Length). Избегает ненужных копий   -  person CodesInChaos    schedule 24.01.2014
comment
В дополнение к ответу ниже, я также укажу, что метод MemoryStream.GetBytes() был бы более правильным, чем вызов GetBuffer(). Первый ограничивает возвращаемые данные фактическим содержимым потока, а второй возвращает полный буфер, включая неиспользованное заполнение в конце.   -  person Peter Duniho    schedule 30.07.2019


Ответы (1)


Вы не перематываете свои MemoryStreams, поэтому хэш вычисляется из пустой последовательности байтов. Использовать

ms1.Position = 0;
ms2.Position = 0;

после звонка Save.

Еще одно замечание: не используйте GetBuffer таким образом. Используйте ToArray, который даст вам массив байтов того же размера, что и длина потока - GetBuffer возвращает необработанный буфер, который (обычно) будет иметь некоторое заполнение, которое вы не хотели бы использовать случайно. Вы можете использовать GetBuffer, если затем убедитесь, что используете только соответствующую его часть, конечно - это позволяет избежать создания новой копии данных.

person Jon Skeet    schedule 11.11.2009