如何通过通道传递压缩字节?

I'm trying to compress file from buffered reader and pass compressed bytes through byte channel, but with poor results :), here's what I came up till now, obviously this don't works...

func Compress(r io.Reader) (<-chan byte) {
    c := make(chan byte)
    go func(){
        var wBuff bytes.Buffer
        rBuff := make([]byte, 1024)
        writer := zlib.NewWriter(*wBuff)
        for {
            n, err := r.Read(rBuff)
            if err != nil && err != io.EOF { panic(err) }
            if n == 0 { break }
            writer.Write(rBuff) // Compress and write compressed data
            // How to send written compressed bytes through channel?
            // as fas as I understand wBuff will eventually contain
            // whole compressed data?
        }
        writer.Close()
        close(c) // Indicate that no more data follows
    }()
    return c
}

Please bear with me, as I'm very new to Go

I suggest to use []byte instead of byte. It is more efficient. Because of concurrent memory accesses it may be necessary to send a copy of the buffer through the channel rather than sending the []byte buffer itself.

You can define a type ChanWriter chan []byte and let it implement the io.Writer interface. Then pass the ChanWriter to zlib.NewWriter.

You can create a goroutine for doing the compression and then immediately return the ChanWriter's channel from your Compress function. If there is no goroutine then there is no reason for the function to return a channel and the preferred return type is io.Reader.

The return type of the Compress function should be changed into something like chan <-BytesWithError. In this case ChanWriter can be defined as type ChanWriter chan BytesWithError.

Ok, I've found working solution: (Feel free to indicate where it can be improved, or maybe I'm doing something wrong?)

func Compress(r io.Reader) (<-chan byte) {
    c := make(chan byte)

    go func(){
        var wBuff bytes.Buffer
        rBuff := make([]byte, 1024)
        writer := zlib.NewWriter(&wBuff)
        for {
            n, err := r.Read(rBuff)

            if err != nil {
                if err != io.EOF {
                    panic(err)
                }
                if n == 0 {
                    break
                }
            }

            writer.Write(rBuff[:n])

            for _, v := range wBuff.Bytes() {
                c <- v
            }

            wBuff.Truncate(0)
        }

        writer.Close()

        for _, v := range wBuff.Bytes() {
            c <- v
        }

        close(c) // Indicate that no more data follows
    }()

    return c
}

Your writer.Write(rBuff) statement always writes len(rBuff) bytes, even when n != len(rBuff).

writer.Write(rBuff[:n])

Also, your Read loop is

for {
    n, err := r.Read(rBuff)
    if err != nil && err != io.EOF {
        panic(err)
    }
    if n == 0 {
        break
    }
    writer.Write(rBuff[:n])
    // ...
}

which is equivalent to

for {
    n, err := r.Read(rBuff)
    if err != nil && err != io.EOF {
        panic(err)
    }
    // !(err != nil && err != io.EOF)
    // !(err != nil) || !(err != io.EOF)
    // err == nil || err == io.EOF
    if err == nil || err == io.EOF {
        if n == 0 {
            break
        }
    }
    writer.Write(rBuff[:n])
    // ...
}

The loop exits prematurely if err == nil && if n == 0.

Instead, write

for {
    n, err := r.Read(rBuf)
    if err != nil {
        if err != io.EOF {
            panic(err)
        }
        if n == 0 {
            break
        }
    }
    writer.Write(rBuf[:n])
    // ...
}

Sending bytes one by one down a channel is not going to be particularly efficient. Another approach that may be more useful would be to return an object implementing the io.Reader interface, implementing the Read() method by reading a block from a original io.Reader and compressing its output before returning it.