mirror of
https://github.com/wneessen/go-mail.git
synced 2024-11-22 13:50:49 +01:00
Merge pull request #111 from wneessen/fix/110_attachment-issues
Fix Attach/EmbedReader and implement Attach/EmbedReadSeeker
This commit is contained in:
commit
6953977d03
2 changed files with 173 additions and 5 deletions
54
msg.go
54
msg.go
|
@ -714,11 +714,22 @@ func (m *Msg) AttachFile(n string, o ...FileOption) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AttachReader adds an attachment File via io.Reader to the Msg
|
// AttachReader adds an attachment File via io.Reader to the Msg
|
||||||
|
//
|
||||||
|
// CAVEAT: For AttachReader to work it has to read all data of the io.Reader
|
||||||
|
// into memory first, so it can seek through it. Using larger amounts of
|
||||||
|
// data on the io.Reader should be avoided. For such, it is recommeded to
|
||||||
|
// either use AttachFile or AttachReadSeeker instead
|
||||||
func (m *Msg) AttachReader(n string, r io.Reader, o ...FileOption) {
|
func (m *Msg) AttachReader(n string, r io.Reader, o ...FileOption) {
|
||||||
f := fileFromReader(n, r)
|
f := fileFromReader(n, r)
|
||||||
m.attachments = m.appendFile(m.attachments, f, o...)
|
m.attachments = m.appendFile(m.attachments, f, o...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AttachReadSeeker adds an attachment File via io.ReadSeeker to the Msg
|
||||||
|
func (m *Msg) AttachReadSeeker(n string, r io.ReadSeeker, o ...FileOption) {
|
||||||
|
f := fileFromReadSeeker(n, r)
|
||||||
|
m.attachments = m.appendFile(m.attachments, f, o...)
|
||||||
|
}
|
||||||
|
|
||||||
// AttachHTMLTemplate adds the output of a html/template.Template pointer as File attachment to the Msg
|
// AttachHTMLTemplate adds the output of a html/template.Template pointer as File attachment to the Msg
|
||||||
func (m *Msg) AttachHTMLTemplate(n string, t *ht.Template, d interface{}, o ...FileOption) error {
|
func (m *Msg) AttachHTMLTemplate(n string, t *ht.Template, d interface{}, o ...FileOption) error {
|
||||||
f, err := fileFromHTMLTemplate(n, t, d)
|
f, err := fileFromHTMLTemplate(n, t, d)
|
||||||
|
@ -762,11 +773,22 @@ func (m *Msg) EmbedFile(n string, o ...FileOption) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmbedReader adds an embedded File from an io.Reader to the Msg
|
// EmbedReader adds an embedded File from an io.Reader to the Msg
|
||||||
|
//
|
||||||
|
// CAVEAT: For AttachReader to work it has to read all data of the io.Reader
|
||||||
|
// into memory first, so it can seek through it. Using larger amounts of
|
||||||
|
// data on the io.Reader should be avoided. For such, it is recommeded to
|
||||||
|
// either use AttachFile or AttachReadSeeker instead
|
||||||
func (m *Msg) EmbedReader(n string, r io.Reader, o ...FileOption) {
|
func (m *Msg) EmbedReader(n string, r io.Reader, o ...FileOption) {
|
||||||
f := fileFromReader(n, r)
|
f := fileFromReader(n, r)
|
||||||
m.embeds = m.appendFile(m.embeds, f, o...)
|
m.embeds = m.appendFile(m.embeds, f, o...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EmbedReadSeeker adds an embedded File from an io.ReadSeeker to the Msg
|
||||||
|
func (m *Msg) EmbedReadSeeker(n string, r io.ReadSeeker, o ...FileOption) {
|
||||||
|
f := fileFromReadSeeker(n, r)
|
||||||
|
m.embeds = m.appendFile(m.embeds, f, o...)
|
||||||
|
}
|
||||||
|
|
||||||
// EmbedHTMLTemplate adds the output of a html/template.Template pointer as embedded File to the Msg
|
// EmbedHTMLTemplate adds the output of a html/template.Template pointer as embedded File to the Msg
|
||||||
func (m *Msg) EmbedHTMLTemplate(n string, t *ht.Template, d interface{}, o ...FileOption) error {
|
func (m *Msg) EmbedHTMLTemplate(n string, t *ht.Template, d interface{}, o ...FileOption) error {
|
||||||
f, err := fileFromHTMLTemplate(n, t, d)
|
f, err := fileFromHTMLTemplate(n, t, d)
|
||||||
|
@ -1114,15 +1136,37 @@ func fileFromFS(n string) *File {
|
||||||
|
|
||||||
// fileFromReader returns a File pointer from a given io.Reader
|
// fileFromReader returns a File pointer from a given io.Reader
|
||||||
func fileFromReader(n string, r io.Reader) *File {
|
func fileFromReader(n string, r io.Reader) *File {
|
||||||
|
d, err := io.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return &File{}
|
||||||
|
}
|
||||||
|
br := bytes.NewReader(d)
|
||||||
return &File{
|
return &File{
|
||||||
Name: filepath.Base(n),
|
Name: n,
|
||||||
Header: make(map[string][]string),
|
Header: make(map[string][]string),
|
||||||
Writer: func(w io.Writer) (int64, error) {
|
Writer: func(w io.Writer) (int64, error) {
|
||||||
nb, err := io.Copy(w, r)
|
rb, cerr := io.Copy(w, br)
|
||||||
if err != nil {
|
if cerr != nil {
|
||||||
return nb, err
|
return rb, cerr
|
||||||
}
|
}
|
||||||
return nb, nil
|
_, cerr = br.Seek(0, io.SeekStart)
|
||||||
|
return rb, cerr
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileFromReadSeeker returns a File pointer from a given io.ReadSeeker
|
||||||
|
func fileFromReadSeeker(n string, r io.ReadSeeker) *File {
|
||||||
|
return &File{
|
||||||
|
Name: n,
|
||||||
|
Header: make(map[string][]string),
|
||||||
|
Writer: func(w io.Writer) (int64, error) {
|
||||||
|
rb, err := io.Copy(w, r)
|
||||||
|
if err != nil {
|
||||||
|
return rb, err
|
||||||
|
}
|
||||||
|
_, err = r.Seek(0, io.SeekStart)
|
||||||
|
return rb, err
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
124
msg_test.go
124
msg_test.go
|
@ -2667,3 +2667,127 @@ func TestMsg_GetBccString(t *testing.T) {
|
||||||
`"Toni Cc" <bcc@example.com>"`, fh[0])
|
`"Toni Cc" <bcc@example.com>"`, fh[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestMsg_AttachEmbedReader_consecutive tests the Msg.AttachReader and Msg.EmbedReader
|
||||||
|
// methods with consecutive calls to Msg.WriteTo to make sure the attachments are not
|
||||||
|
// lost (see Github issue #110)
|
||||||
|
func TestMsg_AttachEmbedReader_consecutive(t *testing.T) {
|
||||||
|
ts1 := "This is a test string"
|
||||||
|
ts2 := "Another test string"
|
||||||
|
m := NewMsg()
|
||||||
|
m.AttachReader("attachment.txt", bytes.NewBufferString(ts1))
|
||||||
|
m.EmbedReader("embeded.txt", bytes.NewBufferString(ts2))
|
||||||
|
obuf1 := &bytes.Buffer{}
|
||||||
|
obuf2 := &bytes.Buffer{}
|
||||||
|
_, err := m.WriteTo(obuf1)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("WriteTo to first output buffer failed: %s", err)
|
||||||
|
}
|
||||||
|
_, err = m.WriteTo(obuf2)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("WriteTo to second output buffer failed: %s", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(obuf1.String(), "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n") {
|
||||||
|
t.Errorf("Expected file attachment string not found in first output buffer")
|
||||||
|
}
|
||||||
|
if !strings.Contains(obuf2.String(), "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n") {
|
||||||
|
t.Errorf("Expected file attachment string not found in second output buffer")
|
||||||
|
}
|
||||||
|
if !strings.Contains(obuf1.String(), "QW5vdGhlciB0ZXN0IHN0cmluZw==") {
|
||||||
|
t.Errorf("Expected embeded file string not found in first output buffer")
|
||||||
|
}
|
||||||
|
if !strings.Contains(obuf2.String(), "QW5vdGhlciB0ZXN0IHN0cmluZw==") {
|
||||||
|
t.Errorf("Expected embded file string not found in second output buffer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMsg_AttachEmbedReadSeeker_consecutive tests the Msg.AttachReadSeeker and
|
||||||
|
// Msg.EmbedReadSeeker methods with consecutive calls to Msg.WriteTo to make
|
||||||
|
// sure the attachments are not lost (see Github issue #110)
|
||||||
|
func TestMsg_AttachEmbedReadSeeker_consecutive(t *testing.T) {
|
||||||
|
ts1 := []byte("This is a test string")
|
||||||
|
ts2 := []byte("Another test string")
|
||||||
|
m := NewMsg()
|
||||||
|
m.AttachReadSeeker("attachment.txt", bytes.NewReader(ts1))
|
||||||
|
m.EmbedReadSeeker("embeded.txt", bytes.NewReader(ts2))
|
||||||
|
obuf1 := &bytes.Buffer{}
|
||||||
|
obuf2 := &bytes.Buffer{}
|
||||||
|
_, err := m.WriteTo(obuf1)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("WriteTo to first output buffer failed: %s", err)
|
||||||
|
}
|
||||||
|
_, err = m.WriteTo(obuf2)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("WriteTo to second output buffer failed: %s", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(obuf1.String(), "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n") {
|
||||||
|
t.Errorf("Expected file attachment string not found in first output buffer")
|
||||||
|
}
|
||||||
|
if !strings.Contains(obuf2.String(), "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n") {
|
||||||
|
t.Errorf("Expected file attachment string not found in second output buffer")
|
||||||
|
}
|
||||||
|
if !strings.Contains(obuf1.String(), "QW5vdGhlciB0ZXN0IHN0cmluZw==") {
|
||||||
|
t.Errorf("Expected embeded file string not found in first output buffer")
|
||||||
|
}
|
||||||
|
if !strings.Contains(obuf2.String(), "QW5vdGhlciB0ZXN0IHN0cmluZw==") {
|
||||||
|
t.Errorf("Expected embded file string not found in second output buffer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMsg_AttachReadSeeker tests the Msg.AttachReadSeeker method
|
||||||
|
func TestMsg_AttachReadSeeker(t *testing.T) {
|
||||||
|
m := NewMsg()
|
||||||
|
ts := []byte("This is a test string")
|
||||||
|
r := bytes.NewReader(ts)
|
||||||
|
m.AttachReadSeeker("testfile.txt", r)
|
||||||
|
if len(m.attachments) != 1 {
|
||||||
|
t.Errorf("AttachReadSeeker() failed. Number of attachments expected: %d, got: %d", 1,
|
||||||
|
len(m.attachments))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file := m.attachments[0]
|
||||||
|
if file == nil {
|
||||||
|
t.Errorf("AttachReadSeeker() failed. Attachment file pointer is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if file.Name != "testfile.txt" {
|
||||||
|
t.Errorf("AttachReadSeeker() failed. Expected file name: %s, got: %s", "testfile.txt",
|
||||||
|
file.Name)
|
||||||
|
}
|
||||||
|
wbuf := bytes.Buffer{}
|
||||||
|
if _, err := file.Writer(&wbuf); err != nil {
|
||||||
|
t.Errorf("execute WriterFunc failed: %s", err)
|
||||||
|
}
|
||||||
|
if wbuf.String() != string(ts) {
|
||||||
|
t.Errorf("AttachReadSeeker() failed. Expected string: %q, got: %q", ts, wbuf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMsg_EmbedReadSeeker tests the Msg.EmbedReadSeeker method
|
||||||
|
func TestMsg_EmbedReadSeeker(t *testing.T) {
|
||||||
|
m := NewMsg()
|
||||||
|
ts := []byte("This is a test string")
|
||||||
|
r := bytes.NewReader(ts)
|
||||||
|
m.EmbedReadSeeker("testfile.txt", r)
|
||||||
|
if len(m.embeds) != 1 {
|
||||||
|
t.Errorf("EmbedReadSeeker() failed. Number of attachments expected: %d, got: %d", 1,
|
||||||
|
len(m.embeds))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file := m.embeds[0]
|
||||||
|
if file == nil {
|
||||||
|
t.Errorf("EmbedReadSeeker() failed. Embedded file pointer is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if file.Name != "testfile.txt" {
|
||||||
|
t.Errorf("EmbedReadSeeker() failed. Expected file name: %s, got: %s", "testfile.txt",
|
||||||
|
file.Name)
|
||||||
|
}
|
||||||
|
wbuf := bytes.Buffer{}
|
||||||
|
if _, err := file.Writer(&wbuf); err != nil {
|
||||||
|
t.Errorf("execute WriterFunc failed: %s", err)
|
||||||
|
}
|
||||||
|
if wbuf.String() != string(ts) {
|
||||||
|
t.Errorf("EmbedReadSeeker() failed. Expected string: %q, got: %q", ts, wbuf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue