根据 RFC2045 限制行长度的 base64 编码

ste*_*eve 5 base64 go

RFC2045 第 6.8 节规定 base64 输出的最大编码行长度应为 76 个字符或更少。

Golang 流编写base64.NewEncoder器没有任何用于行拆分的选项,如下所示。

package main

import (
    "encoding/base64"
    "io"
    "os"
    "strings"
)


// See https://www.ietf.org/rfc/rfc2045.txt, section 6.8 for notes on maximum line length of 76 characters
func main() {
    data := "It is only the hairs on a gooseberry that prevent it from being a grape! This is long enough to need a line split"
    rdr := strings.NewReader(data)
    wrt := base64.NewEncoder(base64.StdEncoding, os.Stdout)
    io.Copy(wrt, rdr)
}
Run Code Online (Sandbox Code Playgroud)

输出是

SXQgaXMgb25seSB0aGUgaGFpcnMgb24gYSBnb29zZWJlcnJ5IHRoYXQgcHJldmVudCBpdCBmcm9tIGJlaW5nIGEgZ3JhcGUhIEl0IGlzIG9ubHkgdGhlIGhhaXJzIG9uIGEgZ29vc2ViZXJyeSB0aGF0IHByZXZlbnQgaXQgZnJvbSBiZWluZyBhIGdyYXBl
Run Code Online (Sandbox Code Playgroud)

是否有基于流的分割线解决方案?该MIME库报价仅基于字符串的编码选项。

ste*_*eve 0

这是我尝试创建一个简单的Writer. 它考虑到不同数量的输入数据,具有可配置的块长度和分隔符序列。它使用字节片写入块,这有望提高效率。

package main

import (
    "encoding/base64"
    "io"
    "os"
    "strings"
)

func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

type linesplitter struct {
    len   int
    count int
    sep   []byte
    w     io.Writer
}

// NewWriter that splits input every len bytes with a sep byte sequence, outputting to writer w
func (ls *linesplitter) NewWriter(len int, sep []byte, w io.Writer) io.WriteCloser {
    return &linesplitter{len: len, count: 0, sep: sep, w: w}
}

// Split a line in to ls.len chunks with separator
func (ls *linesplitter) Write(in []byte) (n int, err error) {
    writtenThisCall := 0
    readPos := 0
    // Leading chunk size is limited by: how much input there is; defined split length; and
    // any residual from last time
    chunkSize := min(len(in), ls.len-ls.count)
    // Pass on chunk(s)
    for {
        ls.w.Write(in[readPos:(readPos + chunkSize)])
        readPos += chunkSize // Skip forward ready for next chunk
        ls.count += chunkSize
        writtenThisCall += chunkSize

        // if we have completed a chunk, emit a separator
        if ls.count >= ls.len {
            ls.w.Write(ls.sep)
            writtenThisCall += len(ls.sep)
            ls.count = 0
        }
        inToGo := len(in) - readPos
        if inToGo <= 0 {
            break // reached end of input data
        }
        // Determine size of the NEXT chunk
        chunkSize = min(inToGo, ls.len)
    }
    return writtenThisCall, nil
}

func (ls *linesplitter) Close() (err error) {
    return nil
}

// See https://www.ietf.org/rfc/rfc2045.txt, section 6.8 for notes on maximum line length of 76 characters
func main() {
    data := "It is only the hairs on a gooseberry that prevent it from being a grape! This is long enough to need a line split"
    shortData := "hello there"

    var ls linesplitter
    lsWriter := ls.NewWriter(76, []byte("\r\n"), os.Stdout)
    wrt := base64.NewEncoder(base64.StdEncoding, lsWriter)

    for i := 0; i < 10; i++ {
        io.Copy(wrt, strings.NewReader(shortData))
        io.Copy(wrt, strings.NewReader(data))
        io.Copy(wrt, strings.NewReader(shortData))
    }
}
Run Code Online (Sandbox Code Playgroud)

...欢迎提出意见/改进。