2010-08-09 2 views
3

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

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

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

+1

@freelookenstein: из чистого любопытство, почему вы хотите это сделать? Я не говорю, что ты не прав. Мне совершенно любопытно, что ты хочешь делать :)/Мне нравится учиться! –

+0

Я реализую процедуру загрузки, которая может возобновить частичную загрузку. Хотя мне тоже очень любопытно :) – user37078

ответ

6

Вы можете хэш больших объемов данных с использованием 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; 
1

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

+0

Но не будет ли поток памяти занимать n байтов? Скажем, я хочу хэш первого Gigabyte. Разве это не приведет к концерту в ОЗУ? – user37078

+0

Второй абзац упоминает об этом, хотя, по-видимому, недостаточно ясен. Я обновлю его. – user37078

+1

@freelookenstein: Да, MemoryStream использует массив байтов в качестве хранилища, поэтому вместо этого вы можете использовать массив байтов. – Guffa

2
+1

Вам нужно больше кода, чем читать N байтов из файла. Вы должны получить возвращаемое значение из вызова, чтобы определить, сколько байтов было * на самом деле * прочитано в буфере, и повторить вызов до тех пор, пока вы не прочитаете все N байтов. Это распространенная ошибка при использовании метода Read, поэтому вы не должны показывать пример, который просто добавляет к путанице. – Guffa

+0

@ Guffa это направление, а не полное решение. Я согласен с необходимостью фиксировать фактическое количество прочитанных байтов, но почему я должен повторять вызов до тех пор, пока у меня не будет N из них? если файл имеет 20 байтов, а N равно 10, почему 'FileStream.Read' возвращает менее 10? – Andrey

+0

Потому что нет гарантии, что он вернет максимальное количество байтов, которое вы запрашиваете. Прочтите документацию: http://msdn.microsoft.com/en-us/library/system.io.filestream.read.aspx – Guffa

1

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

Следует также отметить, что вы не хотите совершать прямой звонок до Read и assume that the bytes have been read.

Rather, you want to make sure that the number of bytes that are returned are the number of bytes that you requested, and make another call to Read in the event that the number of bytes returned doesn't equal the initial number requested.

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

1

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

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

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

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

Метод 'Read' возвращает количество байтов, фактически помещенных в буфер. Если вы игнорируете возвращаемое значение, вы рискуете получить буфер, содержащий меньше данных, чем вы думаете (в этом случае менее 100 байт). Вы должны зациклиться до тех пор, пока не получите все запрошенные вами данные, или пока метод Read не вернет нуль. – Guffa

+0

@ Guffa: Спасибо! Я отредактирую сообщение, чтобы сослаться на ваш комментарий. – kbrimington

Смежные вопросы