Email 如何为Go中的电子邮件创建嵌套的多部分/MIME信封?

Email 如何为Go中的电子邮件创建嵌套的多部分/MIME信封?,email,go,mime,multipart,Email,Go,Mime,Multipart,我想知道如何在Go中创建电子邮件。下面的代码生成了正确的嵌套实体,但没有正确插入边界 你可以在屏幕上看到一个演示 主程序包 进口( “字节” “fmt” “io” “日志” “数学/兰德” “mime/多部分” “mime/quotedprintable” “net/textproto” ) //多部分/混合 //|-多部分/相关 //| |-多部分/备选方案 //| | |-文本/纯文本 //| |`-text/html //|`-内联线。。 //`-附件。。 func main(){ 正文:

我想知道如何在Go中创建电子邮件。下面的代码生成了正确的嵌套实体,但没有正确插入边界

你可以在屏幕上看到一个演示

主程序包
进口(
“字节”
“fmt”
“io”
“日志”
“数学/兰德”
“mime/多部分”
“mime/quotedprintable”
“net/textproto”
)
//多部分/混合
//|-多部分/相关
//| |-多部分/备选方案
//| | |-文本/纯文本
//| |`-text/html
//|`-内联线。。
//`-附件。。
func main(){
正文:=&bytes.Buffer{}
writer:=多部分NewWriter(正文)
变量部分io.Writer
变量错误
//文本内容
part,err=writer.CreatePart(textproto.MIMEHeader{“内容类型”:{“多部分/可选”})
如果错误!=零{
log.Fatal(错误)
}
childWriter:=多部分NewWriter(部分)
文豪
对于,contentType:=范围[]字符串{“text/plain”、“text/html”}{
子部分,err=CreateQuoteTypePart(儿童编写器,内容类型)
如果错误!=零{
log.Fatal(错误)
}
_,err:=subsection.Write([]字节(“这是一行文本,需要用带引号的printable进行包装才能继续。\r\n\r\n”))
如果错误!=零{
log.Fatal(错误)
}
}
//附件
文件名:=fmt.Sprintf(“文件%d.jpg”,rand.Int31())
part,err=writer.CreatePart(textproto.MIMEHeader{“内容类型”:{“应用程序/八位字节流”},“内容处置”:{“附件;文件名=”+filename}})
如果错误!=零{
log.Fatal(错误)
}
部分写入([]字节(“AABBCCDDEEFF”))
writer.Close()
fmt.Print(`From:Bob

To:Alice party加入其中。

不幸的是,用于编写多部分MIME消息的标准库支持有一个不好的嵌套API。问题是,在创建编写器之前,您必须在头中设置
边界
字符串,但在创建编写器之前,生成的边界字符串显然不可用。所以您必须显式设置边界字符串

这是我的解决方案(),为简洁起见进行了简化。我选择使用外部写入程序的边界来设置内部写入程序的边界,并添加了标签,以便在读取输出时更容易跟踪

package main

import ("bytes"; "fmt"; "io"; "log"; "math/rand"; "mime/multipart"; "net/textproto")

//  multipart/mixed
//  |- multipart/related
//  |  |- multipart/alternative
//  |  |  |- text/plain
//  |  |  `- text/html
//  |  `- inlines..
//  `- attachments..

func main() {
    mixedContent := &bytes.Buffer{}
    mixedWriter := multipart.NewWriter(mixedContent)

    // related content, inside mixed
    var newBoundary = "RELATED-" + mixedWriter.Boundary()
    mixedWriter.SetBoundary(first70("MIXED-" + mixedWriter.Boundary()))

    relatedWriter, newBoundary := nestedMultipart(mixedWriter, "multipart/related", newBoundary)
    altWriter, newBoundary := nestedMultipart(relatedWriter, "mulitpart/alternative", "ALTERNATIVE-" + newBoundary)

    // Actual content alternatives (finally!)
    var childContent io.Writer

    childContent, _ = altWriter.CreatePart(textproto.MIMEHeader{"Content-Type": {"text/plain"}})
    childContent.Write([]byte("This is a line of text\r\n\r\n"))
    childContent, _ = altWriter.CreatePart(textproto.MIMEHeader{"Content-Type": {"text/html"}})
    childContent.Write([]byte("<html>HTML goes here\r\n</html>\r\n"))
    altWriter.Close()

    relatedWriter.Close()

    // Attachments
    filename := fmt.Sprintf("File_%d.jpg", rand.Int31())
    var fileContent io.Writer

    fileContent, _ = mixedWriter.CreatePart(textproto.MIMEHeader{"Content-Type": {"application/octet-stream"}, "Content-Disposition": {"attachment; filename=" + filename}})
    fileContent.Write([]byte("AABBCCDDEEFF"))

    mixedWriter.Close()

    fmt.Print(`From: Bob <bob@example.com>
To: Alice <alias@example.com>
Subject: Formatted text mail
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=`)
    fmt.Print(mixedWriter.Boundary(), "\n\n")
    fmt.Println(mixedContent.String())

}

func nestedMultipart(enclosingWriter *multipart.Writer, contentType, boundary string) (nestedWriter *multipart.Writer, newBoundary string) {

    var contentBuffer io.Writer
    var err error

    boundary = first70(boundary)
    contentWithBoundary := contentType + "; boundary=\"" + boundary + "\""
    contentBuffer, err = enclosingWriter.CreatePart(textproto.MIMEHeader{"Content-Type": {contentWithBoundary}})
    if err != nil {
        log.Fatal(err)
    }

    nestedWriter = multipart.NewWriter(contentBuffer)
    newBoundary = nestedWriter.Boundary()
    nestedWriter.SetBoundary(boundary)
    return
}

func first70(str string) string {
    if len(str) > 70 {
        return string(str[0:69])
    }
    return str
}
主程序包
导入(“字节”;“fmt”;“io”;“日志”;“数学/兰德”;“mime/多部分”;“网络/文本协议”)
//多部分/混合
//|-多部分/相关
//| |-多部分/备选方案
//| | |-文本/纯文本
//| |`-text/html
//|`-内联线。。
//`-附件。。
func main(){
mixedContent:=&bytes.Buffer{}
mixedWriter:=multipart.NewWriter(mixedContent)
//相关内容,内部混合
var newBoundary=“RELATED-”+mixedWriter.Boundary()
mixedWriter.SetBoundary(first70(“MIXED-”+mixedWriter.Boundary()))
relatedWriter,newBoundary:=嵌套多部分(混合编写器,“多部分/相关”,newBoundary)
altWriter,newBoundary:=嵌套多部分(relatedWriter,“多部分/备选方案”,“备选方案-”+newBoundary)
//实际内容备选方案(最后!)
瓦夏尔迪奥。作家
childContent,uz=altWriter.CreatePart(textproto.MIMEHeader{“内容类型”:{“text/plain”})
写入([]字节(“这是一行文本\r\n\r\n”))
childContent,uz=altWriter.CreatePart(textproto.MIMEHeader{“内容类型”:{“text/html”})
childContent.Write([]字节(“HTML在这里\r\n\r\n”))
altWriter.Close()
relatedWriter.Close()
//附件
文件名:=fmt.Sprintf(“文件%d.jpg”,rand.Int31())
var fileContent io.Writer
fileContent,u=mixedWriter.CreatePart(textproto.MIMEHeader{“内容类型”:{“应用程序/八位字节流”},“内容处置”:{“附件;文件名=”+filename})
fileContent.Write([]字节(“AABBCCDDEEFF”))
mixedWriter.Close()
fmt.Print(`From:Bob
致:爱丽丝
主题:格式化文本邮件
MIME版本:1.0
内容类型:多部分/混合;边界=`)
fmt.Print(mixedWriter.Boundary(),“\n\n”)
fmt.Println(mixedContent.String())
}
func nestedMultipart(enclosingWriter*multipart.Writer,contentType,边界字符串)(nestedWriter*multipart.Writer,newBoundary字符串){
var contentBuffer io.Writer
变量错误
边界=前70(边界)
contentWithBoundary:=contentType+“boundary=\”“+boundary+”\“”
contentBuffer,err=enclosingWriter.CreatePart(textproto.MIMEHeader{“内容类型”:{contentWithBoundary})
如果错误!=零{
log.Fatal(错误)
}
nestedWriter=multipart.NewWriter(contentBuffer)
newBoundary=nestedWriter.Boundary()
nestedWriter.SetBoundary(边界)
返回
}
func first70(str字符串)字符串{
如果len(str)>70{
返回字符串(str[0:69])
}
返回str
}

@ColinGodsey如果您愿意,您完全可以给悬赏点数。单击问题下方的“开始悬赏”,然后选择原因“奖励现有答案”。
package main

import ("bytes"; "fmt"; "io"; "log"; "math/rand"; "mime/multipart"; "net/textproto")

//  multipart/mixed
//  |- multipart/related
//  |  |- multipart/alternative
//  |  |  |- text/plain
//  |  |  `- text/html
//  |  `- inlines..
//  `- attachments..

func main() {
    mixedContent := &bytes.Buffer{}
    mixedWriter := multipart.NewWriter(mixedContent)

    // related content, inside mixed
    var newBoundary = "RELATED-" + mixedWriter.Boundary()
    mixedWriter.SetBoundary(first70("MIXED-" + mixedWriter.Boundary()))

    relatedWriter, newBoundary := nestedMultipart(mixedWriter, "multipart/related", newBoundary)
    altWriter, newBoundary := nestedMultipart(relatedWriter, "mulitpart/alternative", "ALTERNATIVE-" + newBoundary)

    // Actual content alternatives (finally!)
    var childContent io.Writer

    childContent, _ = altWriter.CreatePart(textproto.MIMEHeader{"Content-Type": {"text/plain"}})
    childContent.Write([]byte("This is a line of text\r\n\r\n"))
    childContent, _ = altWriter.CreatePart(textproto.MIMEHeader{"Content-Type": {"text/html"}})
    childContent.Write([]byte("<html>HTML goes here\r\n</html>\r\n"))
    altWriter.Close()

    relatedWriter.Close()

    // Attachments
    filename := fmt.Sprintf("File_%d.jpg", rand.Int31())
    var fileContent io.Writer

    fileContent, _ = mixedWriter.CreatePart(textproto.MIMEHeader{"Content-Type": {"application/octet-stream"}, "Content-Disposition": {"attachment; filename=" + filename}})
    fileContent.Write([]byte("AABBCCDDEEFF"))

    mixedWriter.Close()

    fmt.Print(`From: Bob <bob@example.com>
To: Alice <alias@example.com>
Subject: Formatted text mail
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=`)
    fmt.Print(mixedWriter.Boundary(), "\n\n")
    fmt.Println(mixedContent.String())

}

func nestedMultipart(enclosingWriter *multipart.Writer, contentType, boundary string) (nestedWriter *multipart.Writer, newBoundary string) {

    var contentBuffer io.Writer
    var err error

    boundary = first70(boundary)
    contentWithBoundary := contentType + "; boundary=\"" + boundary + "\""
    contentBuffer, err = enclosingWriter.CreatePart(textproto.MIMEHeader{"Content-Type": {contentWithBoundary}})
    if err != nil {
        log.Fatal(err)
    }

    nestedWriter = multipart.NewWriter(contentBuffer)
    newBoundary = nestedWriter.Boundary()
    nestedWriter.SetBoundary(boundary)
    return
}

func first70(str string) string {
    if len(str) > 70 {
        return string(str[0:69])
    }
    return str
}