Read/copy 在 Golang 中从 `io.Reader` 到 `io.Writer` 最多一定数量的字节,或者如果超过一定的字节限制就会出现 return 错误?

Read/copy up to a certain number of bytes from `io.Reader` to `io.Writer` in Golang, or return error if OVER a certain bytes limit?

我在 Golang 中有一个 io.Reader,我想在 运行 io.Copy() 之前或期间仔细检查它的数据大小是否低于预定的最大值以将其保存到磁盘使用 io.Writer.

由于io.Reader中的文件数据理论上可能会很大,如果可以避免的话,我想在这里尽量减少内存使用和处理。
我认为没有像 io.CopyLessThanOrEqualToThisManyBytesOrReturnError() 这样的函数,但我确实注意到 io.ReadFull() 可以做与 return 相反的事情,如果没有足够的字节来填充提供的缓冲区,则会出现错误。

有人对此有解决方案吗?


编辑:
澄清一下,复制一小部分数据是不行的。它要么在超过阈值时失败,要么在低于阈值时工作。

您可以使用 io.CopyN: https://golang.org/pkg/io/#CopyN 如果它成功复制正好 N 个字节,则 return 将出现 nil 错误,否则会出现 io.EOF (或者可能是另一个错误) 如果少于 N 个字节。

例如:

func copyMax(dst io.Writer, src io.Reader, n int64) error
    _, err := io.CopyN(dst, src, n)
    if err != nil {
        // Don't care if there's less available
        return nil
    }

    nextByte := make([]byte, 1)
    nRead, _ := io.ReadFull(src, nextByte)
    if nRead > 0 {
        // There's too much data
        return errors.New("Too much data")
    }

    return nil
}

由于io.Reader接口不知道底层数据的大小或长度,所以这个问题只有一个解决方案:

您可以使用一个最大大小为 nMax(预先确定的最大值)+1 的缓冲区,并且在每次调用您的 CopyLessThanOrEqualToThisManyBytesOrReturnError 函数时,在该函数中读取输入并对其进行缓冲,并且检查此缓冲区长度,如果它小于或等于 nMax 则执行 io.Write,否则 return 错误:

const nMax = 5 // your predetermined maximum

func CopyLessThanOrEqualToThisManyBytesOrReturnError(r io.Reader, w io.Writer) error {
    var buf = make([]byte, nMax+1)
    nRead, e := io.ReadFull(r, buf)
    if nRead > 0 && nRead <= nMax {
        w.Write(buf[:nRead])
        return nil
    }
    if nRead > nMax {
        return fmt.Errorf("there is more data")
    }
    return e
}

喜欢这个工作示例代码 unsing string

package main

import (
    "fmt"
    "io"
    "os"
    "strings"
)

const nMax = 5 // your predetermined maximum

func CopyLessThanOrEqualToThisManyBytesOrReturnError(r io.Reader, w io.Writer) error {
    var buf = make([]byte, nMax+1)
    nRead, e := io.ReadFull(r, buf)
    if nRead > 0 && nRead <= nMax {
        w.Write(buf[:nRead])
        return nil
    }
    if nRead > nMax {
        return fmt.Errorf("there is more data")
    }
    return e
}

func main() {
    r := strings.NewReader("123456789")
    err := CopyLessThanOrEqualToThisManyBytesOrReturnError(r, os.Stdout)
    if err != nil {
        fmt.Println(err) // there is more data
    }

    r = strings.NewReader("123\n")
    err = CopyLessThanOrEqualToThisManyBytesOrReturnError(r, os.Stdout) // 123
    if err != nil {
        fmt.Println(err)
    }

    r = strings.NewReader("")
    err = CopyLessThanOrEqualToThisManyBytesOrReturnError(r, os.Stdout)
    if err != nil {
        fmt.Println(err) // EOF
    }
}

输出:

there is more data
123
EOF

工作示例代码,使用文件:

package main

import (
    "fmt"
    "io"
    "os"
)

const nMax = 5 // your predetermined maximum

func CopyLessThanOrEqualToThisManyBytesOrReturnError(r io.Reader, w io.Writer) error {
    var buf = make([]byte, nMax+1)
    nRead, e := io.ReadFull(r, buf)
    if nRead > 0 && nRead <= nMax {
        w.Write(buf[:nRead])
        return nil
    }
    if nRead > nMax {
        return fmt.Errorf("there is more data")
    }
    return e
}

func main() {
    r, err := os.Open("input.bin")
    if err != nil {
        panic(err)
    }
    defer r.Close()

    w, err := os.Create("output.bin")
    if err != nil {
        panic(err)
    }
    defer w.Close()

    err = CopyLessThanOrEqualToThisManyBytesOrReturnError(r, w)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println("Done.")
}

工作示例代码,使用 []byte:

package main

import (
    "bytes"
    "fmt"
    "io"
)

const nMax = 5 // your predetermined maximum

func CopyLessThanOrEqualToThisManyBytesOrReturnError(r io.Reader, w io.Writer) error {
    var buf = make([]byte, nMax+1)
    nRead, e := io.ReadFull(r, buf)
    if nRead > 0 && nRead <= nMax {
        w.Write(buf[:nRead])
        return nil
    }
    if nRead > nMax {
        return fmt.Errorf("there is more data")
    }
    return e
}

func main() {
    bs := []byte{1, 2, 3, 4, 5}
    r := bytes.NewReader(bs)

    w := &bytes.Buffer{}

    err := CopyLessThanOrEqualToThisManyBytesOrReturnError(r, w)
    if err != nil {
        fmt.Println(err)
    }

    fmt.Println("Done.")
    fmt.Println(w.Bytes())
}

输出:

Done.
[1 2 3 4 5]