我正在构建Blob存储系统,我选择了Go作为编程语言。我创建了一个流,以执行从客户端到Blob服务器的分段文件上传。
流工作正常,但我想从请求正文中进行sha1哈希处理。我需要io。复制身体两次。创建了sha1,但是多部分流之后0字节。
任何想法我该怎么做?
客户上传
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 } ..... }
sha1函数
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
}
上传句柄
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
您不能直接执行此操作,但可以编写在io.Copy上执行哈希处理的包装器
// this works for either a reader or writer, // but if you use both in the same time the hash will be wrong. type Hasher struct { io.Writer io.Reader hash.Hash Size uint64 } func (h *Hasher) Write(p []byte) (n int, err error) { n, err = h.Writer.Write(p) h.Hash.Write(p) h.Size += uint64(n) return } func (h *Hasher) Read(p []byte) (n int, err error) { n, err = h.Reader.Read(p) h.Hash.Write(p[:n]) //on error n is gonna be 0 so this is still safe. return } func (h *Hasher) Sum() string { return hex.EncodeToString(h.Hash.Sum(nil)) } func (h *UploadHandle) Read() (io.Reader, string, int64, error) { var b bytes.Buffer hashedReader := &Hasher{Reader: h.Contents, Hash: sha1.New()} n, err := io.Copy(&b, hashedReader) if err != nil { return nil, "", 0, err } return &b, hashedReader.Sum(), n, nil }
//由于我完全忘记了io.TeeReader存在,因此根据@Dustin的评论更新了版本。
io.TeeReader
func (h *UploadHandle) Read() (io.Reader, string, int64, error) { var b bytes.Buffer hash := sha1.New() n, err := io.Copy(&b, io.TeeReader(h.Contents, hash)) if err != nil { return nil, "", 0, err } return &b, hex.EncodeToString(hash.Sum(nil)), n, nil }