Как корректно написать функцию getBlobReader?

Суть одной программы состоит в том, что она умеет хранить любой бинарный файл, разбивая его на куски фиксированного размера плюс остаток. Куски сохраняются в директорию blobs, где каждый назван по хэшу криптографической функции от содержимого. Хэши хранятся в БД и ассоциированы с именем исходного файла.

Нужно затем воссоздать исходный файл, склеив куски по очереди.
Не получается корректно написать функцию getBlobReader. На входе keys (хэш-суммы), на выходе - один io.ReadCloser, который умеет читать со всех кусков по очереди, пока все не закончатся.

Набросал код для примера:
package main

import (
	"io"
	"log"
	"os"
	"path/filepath"
)

func getBlobReader(keys []string) (io.ReadCloser, error) {
	var rc io.ReadCloser
	var err error
	for _, key := range keys {
		filePath := filepath.Join(".", "blobs", key)

		rc, err = os.Open(filePath)
		if err != nil {
			return nil, err
		}

	}
	return rc, nil
}

func main() {
	keys := []string{
		"2050-part1",
		"2050-part2",
		"2050-part3",
	}

	rc, err := getBlobReader(keys)
	if err != nil {
		log.Fatalln(err)
	}

	_, err = io.Copy(os.Stdout, rc)
	if err != nil {
		log.Fatalln(err)
	}

	log.Println("Done")
}
  • Вопрос задан
  • 36 просмотров
Решения вопроса 1
EvgenyMamonov
@EvgenyMamonov Куратор тега Go
Senior software developer, system architect
Вам нужно реализовать свой io.Reader, и в Read уже вычитывать данные по очереди из частей файла.
Вот работающий пример реализации
package main

import (
    "io"
    "log"
    "os"
    "path/filepath"
)

type BlobReader struct {
    keys       []string
    currentKey uint64
    reader     io.ReadCloser
}

func NewBlobReader(keys []string) *BlobReader {
    return &BlobReader{
        keys: keys,
    }
}

// Read реализация интерфейса io.Reader, чтобы можно было ваш reader использовать в io.Copy
func (br *BlobReader) Read(p []byte) (int, error) {
    var err error

    // открываем каждый файл по очереди
    if br.reader == nil {
        filePath := filepath.Join(".", "blobs", br.keys[br.currentKey])
        br.reader, err = os.Open(filePath)
        if err != nil {
            return 0, err
        }
    }

    // читаем данные из текущего открытого файла, пока данные не закончатся
    n, err := br.reader.Read(p)

    // если данные в файле закончились, закрываем его
    if err == io.EOF {
        br.currentKey++
        br.reader.Close()
        br.reader = nil

        // io.EOF в err должно вернуть только у последнего файла, чтобы io.Copy считал все файлы и не завис на последнем.
        if br.currentKey < uint64(len(br.keys)) {
            err = nil
        }
    }

    return n, err
}

func main() {
    blobReader := NewBlobReader([]string{
        "2050-part1",
        "2050-part2",
        "2050-part3",
    })

    _, err := io.Copy(os.Stdout, blobReader)
    if err != nil {
        log.Fatalln(err)
    }

    log.Println("Done")
}

Еще нужно будет реализовать метод Close у вашего Reader'a чтобы небыло утечек файловых дескрипторов
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы