在C#中的方法中返回FileStream是否安全?

时间:2014-01-06 21:54:23

标签: c# asp.net asp.net-web-api

在ASP.NET WebForms 4.5中,我有一个带有GET方法的WebAPI Controller来获取PDF。

然后在应用程序的业务层中,我有一个API类,其中包含一个方法,该方法包含实际查找并将PDF返回给控制器的逻辑。

所以MyController类基本上有:

public HttpResponseMessage GetStatement(string acctNumber, string stmtDate) {
    MyApi myApi = new MyApi();
    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
    FileStream stream = myApi.GetStatement(acctNumber, stmtDate);
    ...set the response.Content = stream...
    ... set the mime type..
    ... close the stream...
    return response;
}

MyApi课程有:

public FileStream GetStatement(string acctNumber, string stmtDate) {
    ... makes an HttpWebRequest to get a PDF from another system ...
    HttpWebRequest req = WebRequest.Create(......)....
    FileStream stream = new FileStream(accountNumber +"_" + stmtDate + ".pdf", FileMode.Create);
    response.GetResponseStream().CopyTo(stream);
    return stream;
}

API类不在应用程序的Web层中,因为它被软件的其他(非Web)部分使用。

我想我的担心是API方法中没有明确关闭FileStream。我可以在Controller方法中做到这一点,但是当他们从其他区域调用它时,我会依赖其他人做同样的事情。

有没有更好的方法从API方法返回PDF文件?可能只是一个字节数组或类似的东西?优选尽可能少的开销。

谢谢 -

4 个答案:

答案 0 :(得分:8)

您不应该返回文件流,而是返回一个字节数组。这样就可以正确地正确处理对象,而不用担心堆栈中的其他调用方法。

byte[] currentFile = ....

然后您可以按如下方式传送文件,字节数组很容易转换为任何内容。下面的示例适用于MVC4。

return new FileContentResult(currentFile, "application/pdf");

答案 1 :(得分:3)

方法返回FileStream并不常见,希望调用者记得将方法调用放在using语句中。但是不想做出这样的假设是可以理解的。一种替代方法是使用一种有趣的控制形式,在这种情况下,您需要调用者为您提供知道如何处理FileStream的回调函数,然后在{{{}}内包装对该处理程序的调用。 1}}陈述。

using

但是,这会在您的用例中需要一些额外的hackery,因为您正在返回其内容通过流进行响应的响应,因此您需要找到一种方法来使您的消息推出回调返回之前的整个响应。

在这种情况下,最好只对您的方法发出注释,以记录您希望调用者确保流关闭的事实。这就是我们在应用程序中所做的事情,到目前为止它运作良好。

答案 2 :(得分:1)

通常,您应该将FileStream放在using块中,如其他答案所描述的那样,或者相信它会被代码的其他部分处理掉。但是,当您从控制器返回FileStream时,这很棘手。例如:

public ActionResult GetImage()
{            
    Stream stream = //get the stream
    return base.File( stream, "image/png" ); 
}

令人高兴的是,一旦编写了流就会被框架处理掉,因此您无需担心处理它。有关详细信息,请参阅 here

答案 3 :(得分:1)

这不是对您的确切要求的答案,而是考虑分享它,以便它可以让您了解其他方法。

public async Task<HttpResponseMessage> Get(string acctNumber, string stmtDate)
{
    HttpResponseMessage response2 = new HttpResponseMessage();

    HttpClient client= new HttpClient();
    string url = "http://localhost:9090/BusinessLayer?acctNumber=" + acctNumber + "&stmtDate=" + stmtDate;
    // NOTE 1: here we are only reading the response1's headers and not the body. The body of response1 would be later be 
    // read by response2.
    HttpResponseMessage response1 = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);

    // NOTE 2: here we need not close response1's stream as Web API would close it when disposing
    // response2's stream content.
    response2.Content = new StreamContent(await response1.Content.ReadAsStreamAsync());
    response2.Content.Headers.ContentLength = response1.Content.Headers.ContentLength;
    response2.Content.Headers.ContentType = response1.Content.Headers.ContentType;

    return response2;
}