Как мне хэшировать первые N байтов файла? - PullRequest
3 голосов
/ 09 августа 2010

Используя .net, я хотел бы иметь возможность хэшировать первые N байтов потенциально больших файлов, но я не могу найти способ сделать это.

Функция ComputeHash (I 'm использует SHA1), принимает байтовый массив или поток, но поток кажется лучшим способом сделать это, так как я бы предпочел не загружать потенциально большой файл в память.

Для ясности: Я не хочу загружать в память потенциально большой кусок данных, если смогу помочь.Если размер файла составляет 2 ГБ, а я хочу хэшировать первые 1 ГБ, это много ОЗУ!

Ответы [ 5 ]

6 голосов
/ 09 августа 2010

Вы можете хэшировать большие объемы данных с помощью CryptoStream - примерно так должно работать:

var sha1 = SHA1Managed.Create();

FileStream fs = \\whatever
using (var cs = new CryptoStream(fs, sha1, CryptoStreamMode.Read))
{
    byte[] buf = new byte[16];
    int bytesRead = cs.Read(buf, 0, buf.Length);
    long totalBytesRead = bytesRead;

    while (bytesRead > 0 && totalBytesRead <= maxBytesToHash)
    {
        bytesRead = cs.Read(buf, 0, buf.Length);
        totalBytesRead += bytesRead;
    }
}

byte[] hash = sha1.Hash;
2 голосов
/ 09 августа 2010
1 голос
/ 09 августа 2010

Если вас беспокоит сохранение слишком большого объема данных в памяти, вы можете создать потоковую оболочку, которая ограничивает максимальное число прочитанных байтов.

Не выполняя всю работу, вот примерная плита котла, которую вы могли бы использовать, чтобы начать работу.

Редактировать: Пожалуйста, просмотрите комментарии для рекомендаций по улучшению этой реализации. Конец редактирования

public class LimitedStream : Stream
{
    private int current = 0;
    private int limit;
    private Stream stream;
    public LimitedStream(Stream stream, int n)
    {
        this.limit = n;
        this.stream = stream;
    }

    public override int ReadByte()
    {
        if (current >= limit)
            return -1;

        var numread = base.ReadByte();
        if (numread >= 0)
            current++;

        return numread;
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        count = Math.Min(count, limit - current);
        var numread = this.stream.Read(buffer, offset, count);
        current += numread;
        return numread;
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotImplementedException();
    }

    public override void SetLength(long value)
    {
        throw new NotImplementedException();
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new NotImplementedException();
    }

    public override bool CanRead
    {
        get { return true; }
    }

    public override bool CanSeek
    {
        get { return false; }
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void Flush()
    {
        throw new NotImplementedException();
    }

    public override long Length
    {
        get { throw new NotImplementedException(); }
    }

    public override long Position
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        if (this.stream != null)
        {
            this.stream.Dispose();
        }
    }
}

Ниже приведен пример используемого потока, который переносит файловый поток, но ограничивает число прочитанных байтов до указанного предела:

using (var stream = new LimitedStream(File.OpenRead(@".\test.xml"), 100))
{
    var bytes = new byte[1024];
    stream.Read(bytes, 0, bytes.Length);
}
1 голос
/ 09 августа 2010

Как уже отмечали другие, вы должны прочитать первые несколько байтов в массив.

Что также следует отметить, что вы не хотите совершать прямой вызов Read и , предполагая , что байты были прочитаны .

Скорее, вы хотите убедиться, что количество возвращаемых байтов равно количеству запрошенных вами байтов, и сделать еще один вызов Read, если число возвращаемых байтов не равно запрашивается начальный номер.

Кроме того, если у вас довольно большие потоки, вы захотите создать прокси для класса потока , где вы передадите ему базовый поток ( FileStream * 1020). * в этом случае) и переопределите метод Read для переадресации вызова в базовый поток , пока не будет прочитано количество байтов, которое необходимо прочитать. Затем, когда возвращается это количество байтов, вы возвращаете -1, чтобы указать, что больше нет байтов для чтения.

1 голос
/ 09 августа 2010

Откройте файл как FileStream, скопируйте первые n байтов в MemoryStream, затем хэшируйте MemoryStream.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...