2017-01-30 12 views
0

Я пытаюсь понять, как я могу читать содержимое файла, вычислять его хэш и возвращать его байты в один проход. До сих пор я делаю это в два этапа, например.Как вернуть хэш и байты в один шаг в Go?

// calculate file checksum 
hasher := sha256.New() 
f, err := os.Open(fname) 
if err != nil { 
    msg := fmt.Sprintf("Unable to open file %s, %v", fname, err) 
    panic(msg) 
} 
defer f.Close() 
b, err := io.Copy(hasher, f) 
if err != nil { 
    panic(err) 
} 
cksum := hex.EncodeToString(hasher.Sum(nil)) 

// read again (!!!) to get data as bytes array 
data, err := ioutil.ReadFile(fname) 

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

ответ

2

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

hasher := sha256.New() 
f, err := os.Open(fname) 
data := io.TeeReader(f, hasher) 
// Now read from data as usual, which is still a stream. 

Что здесь происходит заключается в том, что любые байты, которые считаются с data (который является Reader, как и файл-объект f), будут сдвинуты на hasher.

Обратите внимание, что hasher будет выдавать правильный хеш только после того, как вы прочитали весь файл через data, и до этого момента. Поэтому, если вам нужен хеш, прежде чем вы решите, хотите ли вы прочитать файл, у вас останутся варианты либо сделать это за два прохода (например, как вы сейчас), либо всегда читать файл, но отбрасывать результат, если проверка хеша не выполнена.

Если вы прочитали файл за два прохода, вы можете, конечно, буферизовать все данные файла в байтовом буфере в памяти. Тем не менее, операционная система обычно кэширует файл, который вы только что прочитали в ОЗУ, в любом случае (если это возможно), поэтому преимущество производительности при выполнении буферизованного двухпроходного решения самостоятельно, а не просто выполнение двух проходов над файлом, вероятно, незначительно.

0

Do data, err := ioutil.ReadFile(fname) первый. У вас будет кусочек байтов. Затем создайте свой хешер и сделайте hasher.Write(data).

+0

Я разместил выше, io.Copy и io.Write предоставляет разные типы данных, int64 и int, поэтому результаты зависят от платформы. Как это решить? – Valentin

+0

Если ваши файлы слишком велики для их размера, чтобы вписаться в int, вы не можете сделать байтовый кусок достаточно большим, чтобы держать свой контент в любом случае, так как длина фрагмента - это int. (И если вы работаете на 32-битной платформе, где это проблема, у вас, вероятно, недостаточно памяти для чтения большого файла.) – andybalholm

1

Вы можете написать байты непосредственно на хешер. Например:

package main 

import (
    "crypto/sha256" 
    "encoding/hex" 
    "io/ioutil" 
) 

func main() { 
    hasher := sha256.New() 

    data, err := ioutil.ReadFile("foo.txt") 
    if err != nil { 
     panic(err) 
    } 

    hasher.Write(data) 
    cksum := hex.EncodeToString(hasher.Sum(nil)) 

    println(cksum) 
} 

Как интерфейс Hash вставляет io.Writer. Это позволяет вам прочитать байты из файла один раз, записать их в хешер, а затем также вернуть их.

+0

, хотя он работает, но он даст непоследовательные результаты, io.Copy возвращает int64, а io.Writer возвращает int. Конечно, я могу бросить позже бывшую, но это заставляет меня думать, что она становится специфичной для архитектуры и приведет к проблемам, когда мне нужно иметь дело с большими файлами. – Valentin

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

  • Нет связанных вопросов^_^