【问题标题】:Stream SqlFileStream via WebAPI通过 WebAPI 流式传输 SqlFileStream
【发布时间】:2013-08-08 14:56:34
【问题描述】:

我已经设置了一个支持 FILESTREAM 的 SQL 数据库,并尝试通过 WebAPI 将使用 SqlFileStream 从数据库中检索到的文件流式传输到浏览器。

由于某种原因它不起作用,但我没有收到正确的错误消息。浏览器只是中止连接,Fiddler 也没有显示任何有用的东西,VS 中似乎没有抛出任何错误。

public HttpResponseMessage Get(Guid id)
{
    if(id == null || id == Guid.Empty)
        return Request.CreateResponse(HttpStatusCode.BadRequest);

    try
    {
        FileStreamContext fsc = null;
        Document document = null;
        using(var transaction = new TransactionScope())
        using (var db = new MyEntities())
        {
            try
            {
                fsc = db.Database.SqlQuery<FileStreamContext>("SELECT [File].PathName() AS InternalPath, GET_FILESTREAM_TRANSACTION_CONTEXT() AS TransactionContext FROM Document WHERE id={0}", id).First();
            }
            catch (Exception e)
            {
                Debug.Print(e.ToString());
            }

            document = db.Documents.Where(doc => doc.ID == id).Single();

            var fileStream = new SqlFileStream(fsc.InternalPath, fsc.TransactionContext, FileAccess.Read);

            HttpResponseMessage response = new HttpResponseMessage();
            response.Content = new StreamContent(fileStream);
            //response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
            //response.Content.Headers.ContentDisposition.FileName = document.FileName;
            response.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(document.ContentType);
            return response;
        }
    }
    catch (Exception e)
    {
        return Request.CreateResponse(HttpStatusCode.BadRequest);
    }
}

我怀疑 TransactionScope 提前关闭可能是个问题?不知道为什么我没有收到任何错误消息。

通过 WebApi 流式传输 SqlFileStream 的正确方法是什么?

【问题讨论】:

    标签: asp.net sql-server asp.net-web-api sqlfilestream


    【解决方案1】:

    除了将内容设置为StreamContent,您是否尝试过在控制器中读取fileStream,将其放入byte 数组,然后使用ByteArrayContent 设置内容?

    var fileStream = new SqlFileStream(fsc.InternalPath, fsc.TransactionContext, FileAccess.Read);
    byte[] fileContent = fileStream.ReadFully(); // you will need to implement ReadFully
    HttpResponseMessage response = new HttpResponseMessage();
    response.Content = new ByteArrayContent(fileContent);
    

    对于ReadFully 的实现,我使用过的here is a link to a Jon Skeet method

    而且我怀疑您对连接关闭太早的说法是正确的。您的 DbContext / ObjectContext 很好地包裹在 using 块中,但我认为这就是问题所在。该上下文在控制器返回HttpResponseMessage 后被释放,但响应只能访问流——它仍然需要读取流并将其转换为byte[],然后再传递回浏览器或客户端。这通常在您使用MemoryStream 或类似名称时自动发生,因为框架仍然可以访问流以读取其内容。但是,在这种情况下,您将在读取流之前处理您的 SQL 连接。

    另一种解决方案可能是为您的DbContext 使用依赖注入,而不是在操作方法中对其进行更新。如果您将 IoC 容器设置为在 HTTP 请求结束时自动处理上下文,那么当框架将 StreamContent 转换为 byte[] 并将其推回时,它应该仍然可用(未处理)通过网络。

    【讨论】:

    • 虽然这有效,但将其读入 byte[] 并不是一个诱人的选择。这样我就完全失去了流媒体的好处,并且在处理大文件时会遇到很大的麻烦。
    • 我现在通过使用 CommitableTransaction 而不是 TransactionScope 解决了这个问题,并在 Controllers dispose 方法的后期处理它。 (而不是使用“使用”块)您能否详细说明您的依赖注入解决方案?这究竟是如何工作的?能举个例子吗?
    • 在 Web 上有许多使用 MVC 和 WebAPI 控制器构造函数注入的 DbContext 的依赖注入/控制生命周期范围反转的示例。你不应该需要我的帮助来找到一个。
    • 好的,谢谢。问题出在事务上,因为 SqlFileStream 不需要 DBContext。
    猜你喜欢
    • 2013-04-27
    • 2014-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-20
    • 2013-04-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多