给 io.Reader 添加前缀

huangapple go评论79阅读模式
英文:

Add prefix to io.Reader

问题

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

我的实现并不特别高效,因为我将数据块的数据缓存在内存中,以便可以计算出数据块的长度,这需要作为头部的一部分。

我看到了一些使用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,所以我对这一点不太担心。

英文:

I've written a little server which receives a blob of data in the form of an io.Reader, adds a header and streams the result back to the caller.

My implementation isn't particularly efficient as I'm buffering the blob's data in-memory so that I can calculate the blob's length, which needs to form part of the header.

I've seen some examples of io.Pipe() with io.TeeReader but they're more for splitting an io.Reader into two, and writing them away in parallel.

The blobs I'm dealing with are around 100KB, so not huge but if my server gets busy, memory's going to quickly become an issue...

Any ideas?

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
}

I appreciate it's not a good idea to return interfaces from functions but this code isn't destined to become an API, so I'm not too concerned with that bit.

答案1

得分: 0

一般来说,确定 io.Reader 中数据的长度的唯一方法是读取直到文件结束。有一些特定类型的数据可以确定其长度。

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 包 确定请求体的内容长度 的方式类似。

英文:

In general, the only way to determine the length of data in an io.Reader is to read until EOF. There are ways to determine the length of the data for specific types.

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
}

This is similar to how the net/http package determines the content length of the request body.

huangapple
  • 本文由 发表于 2017年2月18日 21:42:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/42315899.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定