57 lines
1.3 KiB
Go
57 lines
1.3 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"io"
|
|
)
|
|
|
|
// composeStreams concatenates objects into destination while calculating the
|
|
// MD5 of the exact bytes written. Provider-specific storage clients stay out
|
|
// of this package; callbacks keep this helper independent from biz and SDKs.
|
|
func composeStreams(
|
|
ctx context.Context,
|
|
names []string,
|
|
open func(context.Context, string) (io.ReadCloser, error),
|
|
put func(context.Context, string, io.Reader) error,
|
|
remove func(context.Context, string) error,
|
|
destination string,
|
|
) (string, error) {
|
|
reader, writer := io.Pipe()
|
|
hash := md5.New()
|
|
errCh := make(chan error, 1)
|
|
go func() {
|
|
defer writer.Close()
|
|
for _, name := range names {
|
|
if err := ctx.Err(); err != nil {
|
|
errCh <- err
|
|
return
|
|
}
|
|
file, err := open(ctx, name)
|
|
if err != nil {
|
|
errCh <- err
|
|
return
|
|
}
|
|
_, copyErr := io.Copy(io.MultiWriter(writer, hash), file)
|
|
_ = file.Close()
|
|
if copyErr != nil {
|
|
errCh <- copyErr
|
|
return
|
|
}
|
|
}
|
|
errCh <- nil
|
|
}()
|
|
putErr := put(ctx, destination, reader)
|
|
composeErr := <-errCh
|
|
if putErr != nil {
|
|
_ = remove(ctx, destination)
|
|
return "", putErr
|
|
}
|
|
if composeErr != nil {
|
|
_ = remove(ctx, destination)
|
|
return "", composeErr
|
|
}
|
|
return hex.EncodeToString(hash.Sum(nil)), nil
|
|
}
|