К сожалению, стандартная поддержка библиотек для написания многочастных сообщений MIME имеет плохой API для вложения.Проблема в том, что вы должны установить строку boundary
в заголовке перед созданием модуля записи, но сгенерированная строка границы, очевидно, недоступна до создания модуля записи.Таким образом, вы должны установить граничные строки явно.
Вот мое решение ( работает на игровой площадке Go ), упрощенное для краткости.Я решил использовать внешнюю границу писателя, чтобы установить внутреннюю, и добавил метки, чтобы было легче отслеживать при чтении вывода.
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
}