dpno17028 2019-09-04 15:45
浏览 558

符合RFC2045的有限行长的base64编码

RFC2045 section 6.8 states maximum encoded line length of base64 output should be 76 characters or less.

The Golang stream writer base64.NewEncoder does not have any option for line splitting, as can be seen here.

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

Output is

SXQgaXMgb25seSB0aGUgaGFpcnMgb24gYSBnb29zZWJlcnJ5IHRoYXQgcHJldmVudCBpdCBmcm9tIGJlaW5nIGEgZ3JhcGUhIEl0IGlzIG9ubHkgdGhlIGhhaXJzIG9uIGEgZ29vc2ViZXJyeSB0aGF0IHByZXZlbnQgaXQgZnJvbSBiZWluZyBhIGdyYXBl

Is there a stream-based solution to splitting lines? The MIME library offers only string based encoding options.

  • 写回答

1条回答 默认 最新

  • dscc90150010 2019-09-04 18:49
    关注

    Here's my attempt to create a simple Writer. It takes into account varying amounts of input data, has configurable chunk length and separator sequence. It uses byte-slice writes for the chunks, which will hopefully be efficient.

    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("
    "), 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))
        }
    }
    

    ... comments/improvements welcome.

    评论

报告相同问题?

悬赏问题

  • ¥15 关于#matlab#的问题:期望的系统闭环传递函数为G(s)=wn^2/s^2+2¢wn+wn^2阻尼系数¢=0.707,使系统具有较小的超调量
  • ¥15 FLUENT如何实现在堆积颗粒的上表面加载高斯热源
  • ¥30 截图中的mathematics程序转换成matlab
  • ¥15 动力学代码报错,维度不匹配
  • ¥15 Power query添加列问题
  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 報錯:Person is not mapped,如何解決?
  • ¥15 c++头文件不能识别CDialog
  • ¥15 Excel发现不可读取的内容
  • ¥15 关于#stm32#的问题:CANOpen的PDO同步传输问题