在io.Reader中添加前缀

时间:2017-02-18 13:42:16

标签: go

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

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

我已经看到io.Pipe()io.TeeReader的一些示例,但它们更多的是将io.Reader拆分为两个,并将它们并行写出来。

我正在处理的blob大约是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,所以我不太关心这一点。

1 个答案:

答案 0 :(得分: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
}

这类似于net / http包determines the content length of the request body