如何下载存储在数据库中的大文件..流利的nhibernate

本文关键字:文件 nhibernate 数据库 何下载 下载 存储 | 更新日期: 2023-09-27 18:24:50

使用:C#,.NET 3.5 web表单应用程序,fluent nhibernate 1.1.0.685,SQL Server 2008R2

我有一个网络应用程序,允许用户上传文件,并将其附加到他们正在处理的"案例"中。这些文件以varbinary(MAX)的形式存储在数据库中。

以下是我目前用于下载文件的代码:

...
if (!SiteUserService.HasPermission(Domain.SiteUserService.SitePermissions.ModifyCase, CurrentUser))
    this.AccessDenied();
string attachmentId = Request.QueryString["f"].ToString();
DownloadFileResponse response = CaseService.RetrieveAttachment(attachmentId, CurrentUser.Id);
DownloadAttachment(response.Attachment.ContentType, response.Attachment.FileName, response.FileBytes);
...
protected void DownloadAttachment(string mimeType, string fileName, byte[] file)
{
    Response.Clear();
    Response.ContentType = mimeType;
    Response.AddHeader("content-disposition", string.Format("attachment;filename='"{0}'"", fileName));
    Response.BinaryWrite(file);
    Response.Flush();
    Response.End();
}

这适用于较小的文件。昨天上传了几个文件,大小(字节):2230165、2104051、1024274和2202318。当用户尝试下载这些文件时,他们会收到"请求超时"错误。大多数文件的大小约为45572字节。

有问题的应用程序位于DMZ中,因此我们使用WCF服务进行数据访问,没有直接的SQL调用,因此这样的解决方案不起作用。

我在文件系统中没有物理文件,所以我认为下面的代码不起作用:

System.IO.Stream stream = null;
byte[] buffer = new Byte[10000];
int length;
long dataToRead;
string filePath = context.Request.PhysicalPath;
string fileName  = System.IO.Path.GetFileName(filePath);
try
{
    stream = new System.IO.FileStream(filePath, System.IO.FileMode.Open,
    System.IO.FileAccess.Read,System.IO.FileShare.Read);
    dataToRead = stream 
    context.Response.ContentType = "application/octet-stream";
    context.Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
    while(dataToRead > 0)
    {
        if(context.Response.IsClientConnected)
        {
            length = stream (buffer, 0, 10000);
            context.Response.OutputStream.Write(buffer, 0, length);
            context.Response.Flush();
            buffer= new Byte[10000];
            dataToRead = dataToRead - length;
        }
        else
        {
            dataToRead = -1;
        }
    }
}
catch(Exception ex)
{
    throw(ex);
}
finally
{
    if(stream != null)
    {
        stream ();
    }
}

关于如何允许文件以"块"形式下载,有什么建议吗?

编辑:我也研究过这个解决方案,但不知道如何用DB中的一个字节[]来实现它。

如何下载存储在数据库中的大文件..流利的nhibernate

您必须使用sql server的Streaming api,该api可以封装到Stream中。然后像这个一样使用它

var sqlConn = (SqlConnection)session.Connection;
BlobStream blob = new BlobStream(sqlConn, sqlConn.BeginTransaction(), "dbo", "Uploads", "FileData", "Id", id);
BufferedStream bufferedBlob = new BufferedStream(blob, 8040);
// use bufferedBlob to stream from and to context stream

以下是我最终所做的:

public class DownloadHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        // send the file in 10k chunks -- should help with mem consumption
        Stream stream = null;
        byte[] buffer = new Byte[10000];
        // Length of the file:
        int length;
        // Total bytes to read:
        long dataToRead;
        try
        {
            CaseService svc = new CaseService();
            // Retrieve the attachment
            DownloadFileResponse response = svc.RetrieveAttachment(context.Request["f"].ToString(), context.Request["u"].ToString());
            AttachmentContract file = response.Attachment;
            stream = new MemoryStream(response.FileBytes);
            // Total bytes to read:
            dataToRead = Convert.ToInt64(file.FileSize);
            context.Response.ContentType = "application/octet-stream";
            context.Response.AddHeader("Content-Disposition", "attachment; filename=" + file.FileName);
            // Read the bytes.
            while (dataToRead > 0)
            {
                // Verify that the client is connected.
                if (context.Response.IsClientConnected)
                {
                    // Read the data in buffer.
                    length = stream.Read(buffer, 0, 10000);
                    // Write the data to the current output stream.
                    context.Response.OutputStream.Write(buffer, 0, length);
                    // Flush the data to the HTML output.
                    context.Response.Flush();
                    buffer = new Byte[10000];
                    dataToRead = dataToRead - length;
                }
                else
                {
                    //prevent infinite loop if user disconnects
                    dataToRead = -1;
                }
            }
        }
        catch (Exception)
        {
            // Trap the error, if any.
            throw;
        }
        finally
        {
            if (stream != null)
            {
                //Close the file.
                stream.Close();
            }
        }
    }
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

Web.config:

<system.web>
    ...
    <httpRuntime maxRequestLength="51200" />
    ...
</system.web>
<system.serviceModel>
    ...
    <bindings configSource="Config'Wcf'bindings.config" />
    ...
</system.serviceModel>

bindings.config:

<binding name="reliableBinding" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="1048576" maxReceivedMessageSize="4194304" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true">
    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="4194304" maxBytesPerRead="51200" maxNameTableCharCount="4194304" />
    <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="true" />
    <security mode="None">
        <transport clientCredentialType="None" proxyCredentialType="None" realm="" />
        <message clientCredentialType="UserName" algorithmSuite="Default" />
    </security>
</binding>