Downloading large files, saved in Database

China☆狼群 提交于 2019-12-19 10:42:00

问题


We have a lot of files, saved as binary in our SQL Server database. I have made an .ashx file, that delivers these files, to the users. Unfortunately, when the files become rather large, it will fail, with the following error:

Overflow or underflow in the arithmetic operation

I assume it runs out of memory, as I load the binary into a byte[].

So, my question is, how can I make this functionality, read in chunks (maybe?), when it is from a database table? It also seems like Response.TransmitFile() is a good option, but again, how would this work with a database?

The DB.GetReposFile(), in the code beneath, gets the file from the database. There are various fields, for the entry: Filename, ContentType, datestamps and the FileContent as varbinary.

This is my function, to deliver the file:

context.Response.Clear();
try
{
    if (!String.IsNullOrEmpty(context.Request.QueryString["id"]))
    {
        int id = Int32.Parse(context.Request.QueryString["id"]);
        DataTable dtbl = DB.GetReposFile(id);
        string FileName = dtbl.Rows[0]["FileName"].ToString();
        string Extension = FileName.Substring(FileName.LastIndexOf('.')).ToLower();
        context.Response.ContentType = ReturnExtension(Extension);
        context.Response.AddHeader("Content-Disposition", "attachment; filename=" + FileName);

        byte[] buffer = (byte[])dtbl.Rows[0]["FileContent"];
        context.Response.OutputStream.Write(buffer, 0, buffer.Length);
    }
    else
    {
        context.Response.ContentType = "text/html";
        context.Response.Write("<p>Need a valid id</p>");
    }
}
catch (Exception ex)
{
    context.Response.ContentType = "text/html";
    context.Response.Write("<p>" + ex.ToString() + "</p>");
}

Update: The function I ended up with, is the one listed below. DB.GetReposFileSize() simply gets the content Datalength, as Tim mentions. I call this function, in the original code, instead of these two lines:

byte[] buffer = (byte[])dtbl.Rows[0]["FileContent"];
context.Response.OutputStream.Write(buffer, 0, buffer.Length);

New download function:

private void GetFileInChunks(HttpContext context, int ID)
    {
        //string path = @"c:\somefile.txt";
        //FileInfo file = new FileInfo(path);
        int len = DB.GetReposFileSize(ID);
        context.Response.AppendHeader("content-length", len.ToString());
        context.Response.Buffer = false;


        //Stream outStream = (Stream)context.Response.OutputStream;

        SqlConnection conn = null;
        string strSQL = "select FileContent from LM_FileUploads where ID=@ID";
        try
        {
            DB.OpenDB(ref conn, DB.DatabaseConnection.PDM);
            SqlCommand cmd = new SqlCommand(strSQL, conn);
            cmd.Parameters.AddWithValue("@ID", ID);

            SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess);
            reader.Read();
            byte[] buffer = new byte[1024];
            int bytes;
            long offset = 0;

            while ((bytes = (int)reader.GetBytes(0, offset, buffer, 0, buffer.Length)) > 0)
            {
                // TODO: do something with `bytes` bytes from `buffer`
                context.Response.OutputStream.Write(buffer, 0, buffer.Length);

                offset += bytes;
            }
        }

        catch (Exception ex)
        {
            throw ex;
        }
        finally
        {
            DB.CloseDB(ref conn);
        }
    }

回答1:


You can use DATALENGTH to get the size of the VARBINARY and stream it for instance with a SqldataReader and it's Read-or ReadBytes-Method.

Have a look at this answer to see an implementation: Best way to stream files in ASP.NET



来源:https://stackoverflow.com/questions/12140836/downloading-large-files-saved-in-database

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!