Golang io.copy请求主体两次 [英] Golang io.copy twice on the request body
问题描述
我正在构建一个blob存储系统,并且选择Go作为编程语言。
我创建了一个流,用于将多部分文件从客户端上传到blob服务器。
流正常,但我想从sha1生成一个sha1哈希请求正文。我需要io.Copy身体两次。 sha1被创建,但多部分流之后为0字节。
- 创建哈希
- 对于以多部分流式播放正文
任何想法我可以做到这一点吗?
< (c * Client)上传(h * UploadHandle)(* PutResult,错误){
body,bodySize,err:= h.Read()
if err!= nil {
return nil,err
}
//创建sha1从
的字节开始散列$ dropRef,err:= drop.Sha1FromReader(body)
if err!= nil {
return nil,err
}
bodyReader,bodyWriter:= io.Pipe()
writer:= multipart.NewWriter(bodyWriter)
$ b errChan:= make(chan error,1)
go func ){
defer bodyWriter.Close()
part,err:= writer.CreateFormFile(dropRef,dropRef)
if err!= nil {
errChan < - err
返回
}
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
func Sha1FromReader(src io.Reader)错误){
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中的散列
//这适用于读者或作者,
//但如果您在同一时间同时使用哈希将是错的。
类型哈希结构{
io.Writer
io.Reader
hash.Hash
大小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)
返回
}
func(h * Hasher)读取(p []字节)(n int,err错误){
n,err = h.Reader.Read(p)
h.Hash.Write(p [:n])//如果错误n是0,所以这仍然是安全的。
返回
}
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
}
//更新版本基于@ Dustin的评论,因为我完成忘了 io.TeeReader
存在。
$ b $ pre $
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
}
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.
- For creating the hash
- For streaming the body as multipart
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
}
You can't do that directly but you can write a wrapper that does the hashing on 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
}
// updated version based on @Dustin's comment since I complete forgot io.TeeReader
existed.
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
}
这篇关于Golang io.copy请求主体两次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!