From 5f28dbb987e7cc1f21680def66d92503677eaf13 Mon Sep 17 00:00:00 2001 From: Sergey Shtripling Date: Wed, 1 Apr 2020 14:28:47 +0300 Subject: [PATCH] Save io.Reader stream as attachment without needing to save tmp file. --- message.go | 33 +++++++++++++ message_stream_attach_test.go | 93 +++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 message_stream_attach_test.go diff --git a/message.go b/message.go index 4bffb1e..aee9ff8 100644 --- a/message.go +++ b/message.go @@ -311,6 +311,29 @@ func (m *Message) appendFile(list []*file, name string, settings []FileSetting) return append(list, f) } +func (m *Message) appendStream(list []*file, fileName string, src io.Reader, settings []FileSetting) []*file { + f := &file{ + Name: filepath.Base(fileName), + Header: make(map[string][]string), + CopyFunc: func(w io.Writer) error { + if _, err := io.Copy(w, src); err != nil { + return err + } + return nil + }, + } + + for _, s := range settings { + s(f) + } + + if list == nil { + return []*file{f} + } + + return append(list, f) +} + // Attach attaches the files to the email. func (m *Message) Attach(filename string, settings ...FileSetting) { m.attachments = m.appendFile(m.attachments, filename, settings) @@ -320,3 +343,13 @@ func (m *Message) Attach(filename string, settings ...FileSetting) { func (m *Message) Embed(filename string, settings ...FileSetting) { m.embedded = m.appendFile(m.embedded, filename, settings) } + +// Attach file to a message from a io.Reader object +func (m *Message) AttachStream(src io.Reader, fileName string, settings ...FileSetting) { + m.attachments = m.appendStream(m.attachments, fileName, src, settings) +} + +// Embed file to a message from a io.Reader object +func (m *Message) EmbedStream(src io.Reader, fileName string, settings ...FileSetting) { + m.embedded = m.appendStream(m.embedded, fileName, src, settings) +} diff --git a/message_stream_attach_test.go b/message_stream_attach_test.go new file mode 100644 index 0000000..5a9753a --- /dev/null +++ b/message_stream_attach_test.go @@ -0,0 +1,93 @@ +package gomail + +import ( + "bytes" + "encoding/base64" + "io" + "path/filepath" + "testing" +) + +func mockCopyStream(name string) (io.Reader, string, FileSetting) { + nm := filepath.Base(name) + b := bytes.NewReader([]byte("Content of " + nm)) + return b, nm, func(f *file) {} +} +func mockCopyStreamWithHeader(m *Message, name string, h map[string][]string) (io.Reader, string, FileSetting) { + rdr, nm, _ := mockCopyStream(name) + return rdr, nm, SetHeader(h) +} + +func TestStreamAttachmentsOnly(t *testing.T) { + m := NewMessage() + m.SetHeader("From", "from@example.com") + m.SetHeader("To", "to@example.com") + m.AttachStream(mockCopyStream("/tmp/test.pdf")) + m.AttachStream(mockCopyStream("/tmp/test.zip")) + + want := &message{ + from: "from@example.com", + to: []string{"to@example.com"}, + content: "From: from@example.com\r\n" + + "To: to@example.com\r\n" + + "Content-Type: multipart/mixed;\r\n" + + " boundary=_BOUNDARY_1_\r\n" + + "\r\n" + + "--_BOUNDARY_1_\r\n" + + "Content-Type: application/pdf; name=\"test.pdf\"\r\n" + + "Content-Disposition: attachment; filename=\"test.pdf\"\r\n" + + "Content-Transfer-Encoding: base64\r\n" + + "\r\n" + + base64.StdEncoding.EncodeToString([]byte("Content of test.pdf")) + "\r\n" + + "--_BOUNDARY_1_\r\n" + + "Content-Type: application/zip; name=\"test.zip\"\r\n" + + "Content-Disposition: attachment; filename=\"test.zip\"\r\n" + + "Content-Transfer-Encoding: base64\r\n" + + "\r\n" + + base64.StdEncoding.EncodeToString([]byte("Content of test.zip")) + "\r\n" + + "--_BOUNDARY_1_--\r\n", + } + + testMessage(t, m, 1, want) +} + +func TestStreamEmbedded(t *testing.T) { + m := NewMessage() + m.SetHeader("From", "from@example.com") + m.SetHeader("To", "to@example.com") + m.EmbedStream(mockCopyStreamWithHeader(m, "image1.jpg", map[string][]string{"Content-ID": {""}})) + m.EmbedStream(mockCopyStream("image2.jpg")) + m.SetBody("text/plain", "Test") + + want := &message{ + from: "from@example.com", + to: []string{"to@example.com"}, + content: "From: from@example.com\r\n" + + "To: to@example.com\r\n" + + "Content-Type: multipart/related;\r\n" + + " boundary=_BOUNDARY_1_\r\n" + + "\r\n" + + "--_BOUNDARY_1_\r\n" + + "Content-Type: text/plain; charset=UTF-8\r\n" + + "Content-Transfer-Encoding: quoted-printable\r\n" + + "\r\n" + + "Test\r\n" + + "--_BOUNDARY_1_\r\n" + + "Content-Type: image/jpeg; name=\"image1.jpg\"\r\n" + + "Content-Disposition: inline; filename=\"image1.jpg\"\r\n" + + "Content-ID: \r\n" + + "Content-Transfer-Encoding: base64\r\n" + + "\r\n" + + base64.StdEncoding.EncodeToString([]byte("Content of image1.jpg")) + "\r\n" + + "--_BOUNDARY_1_\r\n" + + "Content-Type: image/jpeg; name=\"image2.jpg\"\r\n" + + "Content-Disposition: inline; filename=\"image2.jpg\"\r\n" + + "Content-ID: \r\n" + + "Content-Transfer-Encoding: base64\r\n" + + "\r\n" + + base64.StdEncoding.EncodeToString([]byte("Content of image2.jpg")) + "\r\n" + + "--_BOUNDARY_1_--\r\n", + } + + testMessage(t, m, 1, want) +}