msgraph-sdk-dotnet icon indicating copy to clipboard operation
msgraph-sdk-dotnet copied to clipboard

Cannot Download Very Large Attachments via Stream

Open grippstick opened this issue 1 year ago • 4 comments

My attachment processing server has limited memory, but lots of free disk. I can currently only access a File Attachment via the contentBytes property. This property is an array of bytes and is completely loaded in memory.

If the attachment is very large, it uses or exceeds all of my available memory. I would like to be able to access the attachment via stream so I can download it to disk without using all of my memory.

If not a Stream, perhaps a FileDownloadSession that provides parity to FileUploadSession.

grippstick avatar Aug 02 '22 15:08 grippstick

Thanks for raising this @grippstick

Any chance you could share a code snippet of how you are trying to download the FileAttachment using the SDK?

Typically calls to endpoints returning streams have an extra HttpCompletionOption parameter that may be used to avoid loading the entire response in memory by setting it to HttpCompletionOption.ResponseHeadersRead as in the example below.

var photoContent = await graphClient.Me.Photo.Content
    .Request()
    .GetAsync(completionOption: HttpCompletionOption.ResponseHeadersRead);

andrueastman avatar Aug 03 '22 06:08 andrueastman

I do not see a way to get a stream for Message Attachments. The code I am using is:

var attachment = await graphClient.Me
                                .Messages[msgId]
                                .Attachments[attachmentID]
                                .Request()
                                .GetAsync()
                                .ConfigureAwait(false) as FileAttachment;

grippstick avatar Aug 03 '22 15:08 grippstick

Even if I wanted to get the entire MimeFile, I cannot provide the options needed to download efficiently.

var strm = await graphClient.Me.Messages[""].Content.Request().GetAsync().ConfigureAwait(false);

grippstick avatar Aug 04 '22 16:08 grippstick

Hey @grippstick

I believe it should be possible to do this for the message contents.

var strm = await graphClient.Me.Messages["id"].Content.Request().GetAsync(completionOption: HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);

as avaialable in the builder at https://github.com/microsoftgraph/msgraph-sdk-dotnet/blob/c26ebc00031c2f63c6527e9e6b0c376dbf95e782/src/Microsoft.Graph/Generated/requests/IMessageContentRequest.cs#L29

However, there is indeed an issue with the RequestBuilder for the attachement as it is missing a Content segment.

For this you should be able to work around with as we fix the metadata to have this in the request builders by default

var attachmentRequestUrl = graphClient.Me.Messages[""].Attachments[""].AppendSegmentToRequestUrl("/content");
var attachmentRequest = new BaseRequest(attachmentRequestUrl, graphClient);
var streamResponse = await attachmentRequest.SendStreamRequestAsync(null,CancellationToken.None, completionOption: HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);

andrueastman avatar Oct 04 '22 14:10 andrueastman

Metadata issue fixed in v5 and is should be possible to do

var strm = await graphClient.Me.Messages["id"].Content.GetAsync();

andrueastman avatar Mar 15 '23 09:03 andrueastman