Skip to content

Commit ec75674

Browse files
Merge pull request #85 from huntmj01/main
Modifies GraphSender to use Upload Session
2 parents 0dc1a00 + cce9638 commit ec75674

File tree

4 files changed

+100
-49
lines changed

4 files changed

+100
-49
lines changed

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
# Fork of NatchEurope/FluentEmail.Graph
2+
3+
Fork of [NatchEurope/FluentEmail.Graph](https://github.com/NatchEurope/FluentEmail.Graph) that modifies the `GraphSender` to use an upload session for sending emails with attachments that are 3MB or larger. I was receiving a Microsoft Graph API error when trying to use FluentEmail.Graph to send emails with attachments over 3MB.
4+
5+
[Microsoft Docs on using the Graph API to send large attachments](https://docs.microsoft.com/en-us/graph/outlook-large-attachments?tabs=csharp)
6+
7+
Unfortunately, the Microsoft Graph API `Send` method does not have a `SaveSentItems` argument like the `SendMail` method does, so I had to remove the option to disable saving sent items. See [Link 1](https://docs.microsoft.com/en-us/answers/questions/337574/graph-sdk-i-want-to-send-the-saved-draft-mail-but.html), [Link 2](https://docs.microsoft.com/en-us/graph/api/message-send?view=graph-rest-1.0&tabs=http), and [Link 3](https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/743).
8+
19
# FluentEmail.Graph
210

311
Sender for [FluentEmail](https://github.com/lukencode/FluentEmail) that uses [Microsoft Graph API](https://docs.microsoft.com/en-us/graph/api/resources/mail-api-overview?view=graph-rest-1.0).
@@ -29,7 +37,6 @@ Example config in `appsettings.json`
2937
"AppId": "your app id",
3038
"TenantId": "your tenant id",
3139
"Secret": "your secret here",
32-
"SaveSentItems": true
3340
}
3441
}
3542
```

src/FluentEmail.Graph/FluentEmailServicesBuilderExtensions.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,13 @@ public static FluentEmailServicesBuilder AddGraphSender(
2121
this FluentEmailServicesBuilder builder,
2222
string graphEmailClientId,
2323
string graphEmailTenantId,
24-
string graphEmailSecret,
25-
bool saveSentItems = false)
24+
string graphEmailSecret)
2625
{
2726
var options = new GraphSenderOptions
2827
{
2928
ClientId = graphEmailClientId,
3029
TenantId = graphEmailTenantId,
3130
Secret = graphEmailSecret,
32-
SaveSentItems = saveSentItems,
3331
};
3432
return builder.AddGraphSender(options);
3533
}

src/FluentEmail.Graph/GraphSender.cs

Lines changed: 91 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,13 @@
1919
/// </summary>
2020
public class GraphSender : ISender
2121
{
22-
private readonly bool saveSent;
23-
22+
private const int ThreeMb = 3145728;
2423
private readonly GraphServiceClient graphClient;
2524

2625
public GraphSender(GraphSenderOptions options)
2726
{
28-
this.saveSent = options.SaveSentItems ?? true;
29-
30-
ClientSecretCredential spn = new ClientSecretCredential(options.TenantId, options.ClientId, options.Secret);
31-
32-
this.graphClient = new (spn);
27+
ClientSecretCredential spn = new(options.TenantId, options.ClientId, options.Secret);
28+
this.graphClient = new(spn);
3329
}
3430

3531
public SendResponse Send(IFluentEmail email, CancellationToken? token = null)
@@ -41,11 +37,9 @@ public async Task<SendResponse> SendAsync(IFluentEmail email, CancellationToken?
4137
{
4238
try
4339
{
44-
var message = CreateMessage(email);
45-
await this.graphClient.Users[email.Data.FromAddress.EmailAddress]
46-
.SendMail(message, this.saveSent)
47-
.Request()
48-
.PostAsync();
40+
var message = await this.CreateMessage(email);
41+
42+
await this.SendMessage(email, message);
4943

5044
return new SendResponse
5145
{
@@ -61,61 +55,68 @@ await this.graphClient.Users[email.Data.FromAddress.EmailAddress]
6155
}
6256
}
6357

64-
private static Message CreateMessage(IFluentEmail email)
58+
private async Task<Message> CreateMessage(IFluentEmail email)
6559
{
66-
var messageBody = new ItemBody
60+
var templateBody = new ItemBody
6761
{
6862
Content = email.Data.Body,
6963
ContentType = email.Data.IsHtml ? BodyType.Html : BodyType.Text,
7064
};
7165

72-
var message = new Message();
73-
message.Subject = email.Data.Subject;
74-
message.Body = messageBody;
75-
message.From = ConvertToRecipient(email.Data.FromAddress);
76-
message.ReplyTo = CreateRecipientList(email.Data.ReplyToAddresses);
77-
message.ToRecipients = CreateRecipientList(email.Data.ToAddresses);
78-
message.CcRecipients = CreateRecipientList(email.Data.CcAddresses);
79-
message.BccRecipients = CreateRecipientList(email.Data.BccAddresses);
66+
var template = new Message
67+
{
68+
Subject = email.Data.Subject,
69+
Body = templateBody,
70+
From = ConvertToRecipient(email.Data.FromAddress),
71+
ReplyTo = CreateRecipientList(email.Data.ReplyToAddresses),
72+
ToRecipients = CreateRecipientList(email.Data.ToAddresses),
73+
CcRecipients = CreateRecipientList(email.Data.CcAddresses),
74+
BccRecipients = CreateRecipientList(email.Data.BccAddresses),
75+
};
76+
77+
Message draft = await this.graphClient
78+
.Users[email.Data.FromAddress.EmailAddress]
79+
.MailFolders
80+
.Drafts
81+
.Messages
82+
.Request()
83+
.AddAsync(template);
8084

8185
if (email.Data.Attachments != null && email.Data.Attachments.Count > 0)
8286
{
83-
message.Attachments = new MessageAttachmentsCollectionPage();
84-
85-
email.Data.Attachments.ForEach(
86-
a =>
87+
foreach (var a in email.Data.Attachments)
88+
{
89+
if (a.Data.Length < ThreeMb)
8790
{
88-
var attachment = new FileAttachment
89-
{
90-
Name = a.Filename,
91-
ContentType = a.ContentType,
92-
ContentBytes = GetAttachmentBytes(a.Data),
93-
};
94-
95-
message.Attachments.Add(attachment);
96-
});
91+
await this.UploadAttachmentUnder3Mb(email, draft, a);
92+
}
93+
else
94+
{
95+
await this.UploadAttachement3MbOrOver(email, draft, a);
96+
}
97+
}
9798
}
9899

99100
switch (email.Data.Priority)
100101
{
101102
case Priority.High:
102-
message.Importance = Importance.High;
103+
draft.Importance = Importance.High;
103104
break;
104105

105106
case Priority.Normal:
106-
message.Importance = Importance.Normal;
107+
draft.Importance = Importance.Normal;
107108
break;
108109

109110
case Priority.Low:
110-
message.Importance = Importance.Low;
111+
draft.Importance = Importance.Low;
111112
break;
112113

113114
default:
114-
message.Importance = Importance.Normal;
115+
draft.Importance = Importance.Normal;
115116
break;
116117
}
117118

118-
return message;
119+
return draft;
119120
}
120121

121122
private static IList<Recipient> CreateRecipientList(IEnumerable<Address> addressList)
@@ -147,11 +148,61 @@ private static Recipient ConvertToRecipient([NotNull] Address address)
147148
};
148149
}
149150

151+
private async Task UploadAttachmentUnder3Mb(IFluentEmail email, Message draft, Core.Models.Attachment a)
152+
{
153+
var attachment = new FileAttachment
154+
{
155+
Name = a.Filename,
156+
ContentType = a.ContentType,
157+
ContentBytes = GetAttachmentBytes(a.Data),
158+
};
159+
160+
await this.graphClient
161+
.Users[email.Data.FromAddress.EmailAddress]
162+
.Messages[draft.Id]
163+
.Attachments
164+
.Request()
165+
.AddAsync(attachment);
166+
}
167+
150168
private static byte[] GetAttachmentBytes(Stream stream)
151169
{
152170
using var m = new MemoryStream();
153171
stream.CopyTo(m);
154172
return m.ToArray();
155173
}
174+
175+
private async Task UploadAttachement3MbOrOver(IFluentEmail email, Message draft, Core.Models.Attachment a)
176+
{
177+
var attachmentItem = new AttachmentItem
178+
{
179+
AttachmentType = AttachmentType.File,
180+
Name = a.Filename,
181+
Size = a.Data.Length,
182+
};
183+
184+
var uploadSession = await this.graphClient
185+
.Users[email.Data.FromAddress.EmailAddress]
186+
.Messages[draft.Id]
187+
.Attachments
188+
.CreateUploadSession(attachmentItem)
189+
.Request()
190+
.PostAsync();
191+
192+
int maxSliceSize = 320 * 1024;
193+
var fileUploadTask = new LargeFileUploadTask<FileAttachment>(uploadSession, a.Data, maxSliceSize);
194+
195+
await fileUploadTask.UploadAsync();
196+
}
197+
198+
private async Task SendMessage(IFluentEmail email, Message message)
199+
{
200+
await this.graphClient
201+
.Users[email.Data.FromAddress.EmailAddress]
202+
.Messages[message.Id]
203+
.Send()
204+
.Request()
205+
.PostAsync();
206+
}
156207
}
157208
}

src/FluentEmail.Graph/GraphSenderOptions.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,5 @@ public class GraphSenderOptions
1919
/// Gets or sets the secret string previously shared with AAD at application registration to prove the identity of the application (the client) requesting the tokens.
2020
/// </summary>
2121
public string Secret { get; set; }
22-
23-
/// <summary>
24-
/// Gets or sets a value indicating whether to save the message in Sent Items. Default is <c>true</c>.
25-
/// </summary>
26-
public bool? SaveSentItems { get; set; }
2722
}
2823
}

0 commit comments

Comments
 (0)