I am building a blob storage system and i picked Go as the programming language. I create a stream to do a multipart file upload from client to the blob server.
The stream works fine, but i want to make a sha1 hash from the request body. I need to io.Copy the body twice. The sha1 gets created but the multipart streams 0 bytes after that.
any idea how i can do this?
the client upload
func (c *Client) Upload(h *UploadHandle) (*PutResult, error) {
body, bodySize, err := h.Read()
if err != nil {
return nil, err
}
// Creating a sha1 hash from the bytes of body
dropRef, err := drop.Sha1FromReader(body)
if err != nil {
return nil, err
}
bodyReader, bodyWriter := io.Pipe()
writer := multipart.NewWriter(bodyWriter)
errChan := make(chan error, 1)
go func() {
defer bodyWriter.Close()
part, err := writer.CreateFormFile(dropRef, dropRef)
if err != nil {
errChan <- err
return
}
if _, err := io.Copy(part, body); err != nil {
errChan <- err
return
}
if err = writer.Close(); err != nil {
errChan <- err
}
}()
req, err := http.NewRequest("POST", c.Server+"/drops/upload", bodyReader)
req.Header.Add("Content-Type", writer.FormDataContentType())
resp, err := c.Do(req)
if err != nil {
return nil, err
}
.....
}
the sha1 func
func Sha1FromReader(src io.Reader) (string, error) {
hash := sha1.New()
_, err := io.Copy(hash, src)
if err != nil {
return "", err
}
return hex.EncodeToString(hash.Sum(nil)), nil
}
upload handle
func (h *UploadHandle) Read() (io.Reader, int64, error) {
var b bytes.Buffer
hw := &Hasher{&b, sha1.New()}
n, err := io.Copy(hw, h.Contents)
if err != nil {
return nil, 0, err
}
return &b, n, nil
}
I would suggest using an io.TeeReader
if you want to push all reads from the blob through the sha1 concurrently.
bodyReader := io.TeeReader(body, hash)
Now as the bodyReader is consumed during upload, the hash is automatically updated.