3

我已经设置了一个支持 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 的正确方法是什么?

4

1 回答 1

0

您没有尝试将内容设置为 a StreamContent,而是尝试在控制器中读取fileStream,将其放入byte数组中,然后使用 a 设置内容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实现,这里是我使用的 Jon Skeet 方法的链接。

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

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

于 2013-08-09T17:22:04.973 回答