Reputation: 2321
In a ASP.NET MVC app, I am reading a file from SharePoint Online via the REST API and displaying it in the current browser window for the MVC app. I have code that works for smaller files, but for "large files" (I don't know the exact breakpoint) I get an OutOfMemoryException
. Here's the controller code:
public ActionResult Index() {
System.IO.Stream stream = null;
string fileName = "whatever.ext";
string restUri = $"https://myhost.sharepoint.com/sites/mysite/_api/Web/GetFileByServerRelativePath('mypath/{fileName}')/openbinarystream";
string mimeType = MimeMapping.GetMimeMapping(fileName);
using (var webClient = new WebClient()) {
webClient.Credentials = myCredentials;
webClient.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
var uri = new Uri(restUri);
stream = webClient.OpenRead(uri);
}
Response.AppendHeader("Content-Disposition", "inline; filename=" + fileName);
return File(stream, mimeType);
}
I have looked around SO and the internet, but I cannot find a solution.
I started out reading all the data into a byte[]
via method WebClient.DownloadData
. When I got the OutOfMemoryException
, I thought replacing with a Stream
, via method WebClient.OpenRead
, would fix it, but I'm still getting the exception for large files. For smaller files, the behavior is just as I want it. How can I fix the exception?
UPDATE
The example code above is not the real code. The real code has a lower level client class retrieves the stream and passes it up the call stack to, eventually, the controller. The client class does use a using
statement as shown in the example, though. If the stream is closed when the method returns, and the code exits the using
block, then why did it work for smaller files? Furthermore, I actually got my code working by processing the stream manually with a buffer and writing to the response. My current code is closing the stream in a finally
block (in the controller action method--still retrieving the stream from lower level client class). (Note I came back to post my new code as the answer and close this question, but now I'm a little confused.)
Upvotes: 0
Views: 461
Reputation: 4456
You should return from within the using
statement.
public ActionResult Index() {
System.IO.Stream stream = null;
string fileName = "whatever.ext";
string restUri = $"https://myhost.sharepoint.com/sites/mysite/_api/Web/GetFileByServerRelativePath('mypath/{fileName}')/openbinarystream";
string mimeType = MimeMapping.GetMimeMapping(fileName);
Response.AppendHeader("Content-Disposition", "inline; filename=" + fileName);
using (var webClient = new WebClient()) {
webClient.Credentials = myCredentials;
webClient.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
var uri = new Uri(restUri);
stream = webClient.OpenRead(uri);
return File(stream, mimeType);
}
}
What happens is that the stream is read inside the using statement and after it, the whole bulk of bytes is read in the memory.
When you return before the end of the using statement, it can return the results streamed to the caller
Upvotes: 0
Reputation: 415665
Not sure about the OutOfMemory issue, but the you have this:
using (var webClient = new WebClient())
{
stream = webClient.OpenRead(...);
}
return File(stream, mimeType);
That stream
becomes invalid when webClient
is disposed, unless you have something else we can't see that first reads the entire contents into memory... and that could easily explain the OutOfMemory issue.
Upvotes: 1