2017-02-18 135 views
0

我已经写了一个小服务器,它以io.Reader的形式接收一组数据,添加一个头并将结果传回给调用者。向io.Reader添加前缀

我的实现并不是特别有效,因为我在内存中缓冲blob的数据,所以我可以计算blob的长度,这需要构成头部的一部分。

我见过io.Pipe()io.TeeReader的一些例子,但它们更多的是将io.Reader分成两部分,并且将它们并行写入。

我处理的斑点周围100KB,所以不是很大,但如果我的服务器繁忙时,内存是怎么回事迅速成为一个问题......

任何想法?

func addHeader(in io.Reader) (out io.Reader, err error) { 
    buf := new(bytes.Buffer) 
    if _, err = io.Copy(buf, in); err != nil { 
     return 
    } 

    header := bytes.NewReader([]byte(fmt.Sprintf("header:%d", buf.Len()))) 

    return io.MultiReader(header, buf), nil 
} 

我欣赏它的不返回从函数的接口是一个好主意,但是这个代码不注定要成为一个API,所以我不是太在意这一点。

回答

0

通常,确定io.Reader中数据长度的唯一方法是读取直到EOF。有几种方法可以确定特定类型数据的长度。

func addHeader(in io.Reader) (out io.Reader, err error) { 
    n := 0 
    switch v := in.(type) { 
    case *bytes.Buffer: 
    n = v.Len() 
    case *bytes.Reader: 
    n = v.Len() 
    case *strings.Reader: 
    n = v.Len() 
    case io.Seeker: 
    cur, err := v.Seek(0, 1) 
    if err != nil { 
     return nil, err 
    } 
    end, err := v.Seek(0, 2) 
    if err != nil { 
     return nil, err 
    } 
    _, err = v.Seek(cur, 0) 
    if err != nil { 
     return nil, err 
    } 
    n = int(end - cur) 
    default: 
    var buf bytes.Buffer 
    if _, err := buf.ReadFrom(in); err != nil { 
     return nil, err 
    } 
    n = buf.Len() 
    in = &buf 
    } 
    header := strings.NewReader(fmt.Sprintf("header:%d", n)) 
    return io.MultiReader(header, in), nil 
} 

这类似于如何净/ HTTP包determines the content length of the request body

+0

感谢您的回应,Cerise。这是一个io.ReadCloser的封面,所以我想我只需要缓冲它。干杯! – Rob

+0

@Rob io.ReadCloser是一种接口类型。具体类型是别的。试试'fmt.Println(“%T”,in)'来看看具体类型是什么。也许它会是我答案中列出的类型之一,或者可以在不阅读它的情况下提取长度。 –

+0

啊,是的,我会检查底层的实现,看看我能否提取长度。非常感谢! – Rob