2017-10-13 64 views
0

在ASP.NET MVC应用程序中,我正在通过REST API从SharePoint Online中读取文件并将其显示在MVC应用程序的当前浏览器窗口中。我有代码适用于较小的文件,但对于“大文件”(我不知道确切的断点),我得到一个OutOfMemoryException。这里的控制器代码:OutOfMemoryException当返回FileResult与流

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); 
} 

我看了看周围SO和互联网,但我找不到解决方案。

我开始通过方法WebClient.DownloadData读取所有数据到byte[]。当我得到OutOfMemoryException时,我想用方法WebClient.OpenRead代替Stream,可以解决这个问题,但我仍然遇到大文件的例外。对于较小的文件,行为就像我想要的那样。我如何解决这个异常?

UPDATE
上面的示例代码不是真正的代码。真正的代码有一个较低级别的客户端类检索流,并将其传递给调用堆栈,最终传递给控制器​​。尽管如此,客户类确实使用了using语句。如果该方法返回时流关闭,并且代码退出using块,那么为什么它适用于较小的文件?此外,我实际上通过手动处理流并使用缓冲区写入响应来获得我的代码。我当前的代码正在关闭finally块中的流(在控制器操作方法中 - 仍然从较低级别的客户端类中检索流)。 (注意:我回来后我的新代码作为答案,并关闭了这个问题,但现在我有点糊涂了。)

+0

您是否尝试过? https://stackoverflow.com/questions/41961496/how-do-i-increase-maximum-download-size-in-an-mvc-application – ArhiChief

回答

1

不知道的内存溢出的问题,但你有这样的:

using (var webClient = new WebClient()) 
{ 
    stream = webClient.OpenRead(...); 
} 
return File(stream, mimeType); 

stream在处置webClient时变得无效,除非您还有别的东西,我们无法看到第一个将整个内容读入内存中...并且可以轻松解释OutOfMemory问题。

+0

谢谢。请参阅我的更新。当WebClient对象被丢弃时,你确定这个流是无效的吗?如果是这样,有什么可以解释我的代码正在工作?无论哪种方式,这可能是一个有争议的问题,因为我认为重构是一个好主意,所以客户端类将数据写入使用块中的响应。我不喜欢将流传回堆栈,然后不得不处置它。有什么建议么? – neizan

0

您应该从using声明中返回。

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); 
    } 
} 

会发生什么是在using语句内部读取流,并且在它之后读取整个字节的大部分内存。

当您在using语句结束之前返回时,它可以返回流式传输给调用者的结果

+0

所以你说的是ASP。如果我在'using'块中返回流,NET只会将字节读入内存中?这似乎与我在这里的经验相矛盾,因为我现在有一个工作示例,它在使用之外返回(更新中提到的栈更高)。 – neizan

+0

@neizan你确定选择的方法OpenRead返回流不是字节数组? –

+0

是的,根据[文档](https://msdn.microsoft.com/en-us/library/ms144209(v = vs.110).aspx#Anchor_0)。 – neizan