【问题标题】:base64 encoding with limited line length as per RFC2045根据 RFC2045 具有有限行长的 base64 编码
【发布时间】:2019-09-05 17:07:59
【问题描述】:

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)
}

输出是

SXQgaXMgb25seSB0aGUgaGFpcnMgb24gYSBnb29zZWJlcnJ5IHRoYXQgcHJldmVudCBpdCBmcm9tIGJlaW5nIGEgZ3JhcGUhIEl0IGlzIG9ubHkgdGhlIGhhaXJzIG9uIGEgZ29vc2ViZXJyeSB0aGF0IHByZXZlbnQgaXQgZnJvbSBiZWluZyBhIGdyYXBl

是否有基于流的解决方案来分割线? MIME library 仅提供基于字符串的编码选项。

【问题讨论】:

  • 不是基于流的,但也许可以使用 encoding/pem 破解某些东西

标签: go base64


【解决方案1】:

这是我尝试创建一个简单的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))
    }
}

...欢迎 cmets/改进。

【讨论】:

  • "simple Writer class" -- 注意,这不是一个类。 Go 没有课程。最好将其称为(并将其视为)一种类型。
  • 谢谢,别忘了。我认为这是一种作用于类型的方法。
  • io.WriteCloser 是一个接口。接口是类型。您的实现是一种类型,恰好是一个结构,具有必要的方法。
  • 顺便说一句,我认为您没有任何理由返回 io.WriteCloser 而不仅仅是 io.Writer
  • 谢谢。我已将其设为 Writer 并删除了我的 Close 方法,因为它什么都不做。更新github.com/tuck1s/go-smtpproxy/blob/…
猜你喜欢
  • 2019-04-26
  • 1970-01-01
  • 2019-08-15
  • 2019-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多