Golang: как отправить html тело письма с изображением, используя строку base64 в качестве содержимого вложения - PullRequest
0 голосов
/ 18 февраля 2020

У меня есть следующее сообщение, которое я получаю в своем обработчике golang:

{"fromDisplayName":"ForgottenPassword","to":["example@gmail.com"],"cc":[],"bcc":[],"subject":"ForgottenPassword","body":" \t<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"> <html> \t<head> \t\t<meta http-equiv=\"Content-Type\" content=\"text/html charset=UTF-8\" /> \t\t<title>Forgot Password Email</title> \t</head> \t<body style=\"width:100%; border:0; padding:0; margin:0; text-align:center; background-color:#f3f3f3; font-family:Gotham, 'Helvetica Neue', Helvetica, Arial, 'sans-serif'; font-size:1em\">\t\t \t\t<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"width: 100%; max-width: 680px; margin: auto; margin-top: 30px; box-shadow: 0 1px 2px rgba(0,0,0,0.3);\"> \t\t\t<tr> \t\t\t\t<td>\t\t\t\t\t \t\t\t\t\t<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"width: 100%;\"> \t\t\t\t\t\t<tr> \t\t\t\t\t\t\t<td style=\"background-color:#ffffff; height:4em; padding:1.5em;\"> \t\t\t\t\t\t\t\t<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"width: 100%;\"> \t\t\t\t\t\t\t\t\t<tr> \t\t\t\t\t\t\t\t\t\t<td style=\"padding-bottom: 20px; text-align: left;\"></td> \t\t\t\t\t\t\t\t\t</tr> \t\t\t\t\t\t\t\t\t<tr> \t\t\t\t\t\t\t\t\t\t<td style=\"width: 100%; height: 1px; background-color: #ccc;\"></td> \t\t\t\t\t\t\t\t\t</tr> \t\t\t\t\t\t\t\t</table> \t\t\t\t\t\t\t</td> \t\t\t\t\t\t</tr> \t\t\t\t\t</table> \t\t\t\t\t<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%;\"> \t\t\t\t\t\t<tbody> \t\t\t\t\t\t\t<tr> \t\t\t\t\t\t\t\t<td style=\"width:100%; padding:30px; text-align: center; background-color:#ffffff\"> \t\t\t\t\t\t\t\t\t<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\"> \t\t\t\t\t\t\t\t\t\t<tr> \t\t\t\t\t\t\t\t\t\t\t<td style=\"padding: 40px 40px 0px 40px; text-align: center; font-size:2.5em; font-weight:lighter; mso-height-rule: exactly; line-height: 28px; color: #434795;\">Hi user</td> \t\t\t\t\t\t\t\t\t\t</tr> \t\t\t\t\t\t\t\t\t\t<tr> \t\t\t\t\t\t\t\t\t\t\t<td style=\"text-align: center; font-size:1em; padding-top:0.8em; padding-bottom:2em; color:#999999\">You're already a user, so there is no need to sign up.</td> \t\t\t\t\t\t\t\t\t\t</tr> \t\t\t\t\t\t\t\t\t\t<tr> \t\t\t\t\t\t\t\t\t\t\t<td style=\"text-align: center; font-size:1em; padding-top:0.8em; padding-bottom:2em; color:#999999\">If you forgot your password, you can reset it .</td> \t\t\t\t\t\t\t\t\t\t</tr> \t\t\t\t\t\t\t\t\t\t<tr> \t\t\t\t\t\t\t\t\t\t\t<td style=\"text-align: center;\"> \t\t\t\t\t\t\t\t\t\t\t\t<a><img src=\"cid:278da187427a4bdcaceb76781f4a3753\" alt=\"forgot password\" width=\"100\" height=\"60\" border=\"0\"></a> \t\t\t\t\t\t\t\t\t\t\t</td> \t\t\t\t\t\t\t\t\t\t</tr> \t\t\t\t\t\t\t\t\t</table> \t\t\t\t\t\t\t\t\t<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\"> \t\t\t\t\t\t\t\t\t\t<tr> \t\t\t\t\t\t\t\t\t\t\t<td> \t\t\t\t\t\t\t\t\t\t\t\t<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\"> \t\t\t\t\t\t\t\t\t\t\t\t\t<tr> \t\t\t\t\t\t\t\t\t\t\t\t\t\t<td style = \"font-size:0.75em; padding-top:3.5em; padding-bottom:1.25em; text-align: center; color:#999999\">If you received this email by mistake, you can just delete it.</td> \t\t\t\t\t\t\t\t\t\t\t\t\t</tr> \t\t\t\t\t\t\t\t\t\t\t\t</table> \t\t\t\t\t\t\t\t\t\t\t</td> \t\t\t\t\t\t\t\t\t\t</tr> \t\t\t\t\t\t\t\t\t</table> \t\t\t\t\t\t\t\t\t<table cellspacing=\"0\" cellpadding=\"0\" text-align=\"center\" align=\"center\" bgcolor=\"#f3f3f3\" width=\"100%\" style=\"max-width: 680px\"> \t\t\t\t\t\t\t\t\t\t <tr> \t\t\t\t\t\t\t\t\t\t\t<td style=\" font-size: 0.75em; text-align: center; mso-height-rule: exactly; line-height:17px; padding-top: 20px;\"> \t\t\t\t\t\t\t\t\t\t\t\t<span style=\"color: #999999;\">We don't check this mailbox, so please don't reply to this message.</span><br> \t\t\t\t\t\t\t\t\t\t\t\t<span class=\"mobile-link--footer;\" style=\"color: #999999;\"></span><br><br> \t\t\t\t\t\t\t\t\t\t\t</td> \t\t\t\t\t\t\t\t\t\t</tr> \t\t\t\t\t\t\t\t\t</table> \t\t\t\t\t\t\t\t</td> \t\t\t\t\t\t\t</tr> \t\t\t\t\t\t</tbody> \t\t\t\t\t</table>\t\t\t\t\t \t\t\t\t</td>\t \t\t\t</tr> \t\t</table>\t\t \t</body> </html>","isHtmlBody":true,"attachments":[{"fileName":"click_button.png","content":"iVBORw0KGgoAAAANSUhEUgAAAGQAAAA8CAYAAACQPx/OAAAACXBIWXMAAAsTAAALEwEAmpwYAAAE92lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoTWFjaW50b3NoKSIgeG1wOkNyZWF0ZURhdGU9IjIwMTktMDYtMTdUMTM6MTA6MjcrMDI6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDE5LTA2LTE3VDEzOjEzOjQwKzAyOjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDE5LTA2LTE3VDEzOjEzOjQwKzAyOjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplNTY1ZWM0ZC0xNWEwLTQ1YTQtOWM5Ni1hMjgxNTgzNjNmYTciIHhtcE1NOkRvY3VtZW50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","contentType":"image/png","contentDisposition":"inline","cid":"278da187427a4bdcaceb76781f4a3753"}]}

Моя цель - отправить информацию в сообщении в виде сообщения электронной почты через smtp Google.

Я могу отправить сообщение, но содержимое изображения в сообщении (строка base64) не встраивается в тело письма html. когда я отправляю одно и то же сообщение, используя nodejs и nodemailer - изображение отображается правильно.

как мне этого добиться?

это пример одного из подходов, которые я пробовал и выполняет не показывать изображение в теле письма Ullrich comments Поскольку в моем json содержимое изображения уже закодировано в base64, и библиотека поместит поверх него еще один слой base64, поэтому мне пришлось сначала декодировать его base64 следующим образом:

func EmailServiceBusinessLogic(receivedMsg emailModels.ReceivedMsgModel) error {

    mail := email.NewEmail()

    for _, v := range receivedMsg.Attachments {
        if v.FileName == "" {
            fmt.Println("received empty file name for attachment")
            continue
        }
        if v.Cid == "" {
            fmt.Println("received empty cid for file name: ", v.FileName)
            continue
        }
        if v.ContentType == "" {
            fmt.Println("received empty ContentType for file name: ", v.FileName)
            continue
        }
        if v.Content == "" {
            fmt.Println("received empty Content for file name: ", v.FileName)
            continue
        }

        // Base64 Standard Decoding <-this is the part that was missing!!
        sDec, err := base64.StdEncoding.DecodeString(v.Content)
        if err != nil {
            fmt.Printf("Error decoding Content: %s ", err.Error())
            continue
        }

        mail.Attach(strings.NewReader(string(sDec)), v.Cid, v.ContentType)
    }

    if receivedMsg.FromDisplayName == "" {
        fmt.Println("received empty FromDisplayName: aborting send")
        return errors.New("received empty FromDisplayName: aborting send")

    }
    fromWithoutWhiteSpaces := strings.Join(strings.Fields(receivedMsg.FromDisplayName), "")

    mail.From = fromWithoutWhiteSpaces + "@dont-reply.com"
    mail.To = receivedMsg.To
    mail.Bcc = receivedMsg.Bcc
    mail.Cc = receivedMsg.Cc
    mail.Subject = receivedMsg.Subject
    if receivedMsg.IsHtmlBody {
        mail.HTML = []byte(receivedMsg.Body)
    } else {
        mail.Text = []byte(receivedMsg.Body)
    }

    err := mail.Send(emailGlobals.SmtpConf.Body[0].Value.Host+":587", smtp.PlainAuth("", emailGlobals.SmtpConf.Body[0].Value.Username, emailGlobals.SmtpConf.Body[0].Value.Password, emailGlobals.SmtpConf.Body[0].Value.Host))
    if err != nil {
        return err
    }
}

1 Ответ

0 голосов
/ 18 февраля 2020

Для ссылки на прикрепленное изображение изнутри HTML оно должно иметь Content-Id, т. Е. В HTML оно должно выглядеть так:

`<img src="cid:000001.png">

Глядя на источник Attach в "github.com/jordan-wright/email, который, как я полагаю, вы используете, показывает, что он использует заданное имя файла как Content-Id:

func (e *Email) Attach(r io.Reader, filename string, c string) (a *Attachment, err error) {
    ....
    at.Header.Set("Content-ID", fmt.Sprintf("<%s>", filename))

Таким образом, убедитесь, что v.FileName вы Использование в качестве имени для вашего вложения фактически используется как cid в HTML части.


После того, как к вопросу добавлено больше информации:

.. Единственная информация, которую я имею относительно вложений: имя файла, контент (строка), тип контента (image / png) и атрибут contentDisposition, установленный на inline и cid

В этом случае вы cid а не имя файла.


С еще большей информацией, добавленной к вопросу:

есть следующее сообщение, которое я получаю в моем обработчике golang:

{"fromDisplayName":"Forg...

Похоже, ваше изображение уже закодировано в base64, но библиотека, которую вы используете doe s дополнительная кодировка base64, поэтому сначала нужно ее декодировать. Также библиотека явно использует Content-Type из multipart/mixed, но оно должно быть multipart/related, чтобы изображения были связаны с HTML.

Следующий код работает для меня с JSON из вашего вопроса хранится как mail.json. Обратите внимание, что я пропустил проверку ошибок в нескольких местах, чтобы сосредоточиться на решении. И я не отправляю почту напрямую, а храню ее только в виде строки, чтобы сохранить ее в виде файла EML и проверить. Скорее всего, вам нужно изменить Bytes функцию , чтобы установить multipart/related непосредственно там:

package main

import (
    "github.com/jordan-wright/email"
    "encoding/json"
    "encoding/base64"
    "fmt"
    "os"
    "bytes"
    "strings"
)

type Attachments struct {
    Filename string `json:"filename"`
    Content string `json:"content"`
    ContentType string `json:"contentType"`
    ContentDisposition string `json:"contentDisposition"`
    Cid string `json:"cid"`
}

type Mail struct {
    To []string `json:"to"`
    Subject string `json:"subject"`
    Body string `json:"body"`
    Parts []Attachments `json:"attachments"`
}

func main() {
    f,_ := os.Open("mail.json")
    d := json.NewDecoder(f)
    m := &Mail{}
    d.Decode(&m)
    // fmt.Println(m)

    e := email.NewEmail()
    e.To = m.To
    e.Subject = m.Subject
    e.HTML = []byte(m.Body)
    for _,p := range m.Parts {
        s,_ := base64.StdEncoding.DecodeString(string(p.Content))
        e.Attach(bytes.NewReader(s), p.Cid, p.ContentType)
    }
    s,_ := e.Bytes()
    mail := strings.Replace(string(s),": multipart/mixed;",": multipart/related;",1)
    fmt.Println(mail)
}
...