【发布时间】:2014-10-29 13:07:43
【问题描述】:
我正在构建一个 blob 存储系统,我选择 Go 作为编程语言。 我创建了一个流来执行从客户端到 Blob 服务器的多部分文件上传。
流工作正常,但我想从请求正文中创建一个 sha1 哈希。我需要 io.Copy 身体两次。 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
}
【问题讨论】:
-
-
您真的不需要那么多代码,而且您肯定不需要将 blob 的整个连续副本存储在 RAM 中。我在 RAM 匮乏的设备上对多 GB 的 blob 做了类似的事情。
-
@Dustin 在这种情况下你会怎么做?
-
你对写入缓冲区的内容做了什么?在不将其写入缓冲区的情况下执行此操作。考虑this playground link——它在操场上不起作用,因为您无法创建文件,但可以正确处理任意长的输入,并且内存使用量最少。我为 CAS 做了类似的事情,它也验证了带外哈希,然后在成功关闭文件后重命名文件。
-
作为替代方案,您可以使用这个库https://github.com/hyperboloide/sprocess 来处理文件上传流