Файл поврежден при чтении объекта BLOB из базы данных SQL - PullRequest
0 голосов
/ 05 июля 2018

Все, может кто-нибудь помочь.

У меня проблемы при чтении BLOB-объекта в БД SQL. Файл загружен, но не может открыться из-за повреждения файла. Ошибка появляется с любым типом документа (pdf, docx, jpg и т. Д.).

Код выполняет хранимую процедуру, которая выбирает двоичный файл на основе двух разных параметров.

Вот код:

        protected void Page_Load(object sender, EventArgs e)
    {
        // Request.QueryString["docid"].ToString(); 
        //string DocumentID = Request.QueryString["DocumentID"].ToString();
        string DocumentID = "9163736c-8064-11e8-ab16-2c44fd826130";
        string SessionId = "91494483-8064-11e8-ab16-2c44fd826130";
        //Connection and Parameters
        SqlParameter param1 = null;
        SqlParameter param2 = null;
        SqlConnection conn = new SqlConnection(
           ConfigurationManager.ConnectionStrings["ProcessManagerConnectionString"].ToString());
        SqlCommand cmd = new SqlCommand("sp_getdoc", conn);
        cmd.CommandType = CommandType.StoredProcedure;
        param1 = new SqlParameter("@DocumentID", SqlDbType.NVarChar, 100);
        param2 = new SqlParameter("@SessionId", SqlDbType.NVarChar, 100);
        param1.Direction = ParameterDirection.Input;
        param2.Direction = ParameterDirection.Input;
        param1.Value = DocumentID;
        param2.Value = SessionId;
        cmd.Parameters.Add(param1);
        cmd.Parameters.Add(param2);
        //Open connection and fetch the data with reader
        conn.Open();
        SqlDataReader reader =
          cmd.ExecuteReader(CommandBehavior.CloseConnection);
        if (reader.HasRows)
        {
            reader.Read();
            //
            string doctype = reader["Extension"].ToString();
            string docname = reader["Docname"].ToString();
            //
            Response.Buffer = false;
            Response.ClearHeaders();
            Response.ContentType = doctype;
            Response.AddHeader("Content-Disposition",
                     "attachment; filename=" + docname);
            //
            //Code for streaming the object while writing
            const int ChunkSize = 1024;
            byte[] buffer = new byte[ChunkSize];
            byte[] binary = (reader["Data"]) as byte[];
            MemoryStream ms = new MemoryStream(binary);
            int SizeToWrite = ChunkSize;

            for (int i = 0; i < binary.GetUpperBound(0) - 1; i = i + ChunkSize)
            {
                if (!Response.IsClientConnected) return;
                if (i + ChunkSize >= binary.Length)
                    SizeToWrite = binary.Length - i;
                byte[] chunk = new byte[SizeToWrite];
                ms.Read(chunk, 0, SizeToWrite);
                Response.BinaryWrite(chunk);
                Response.Flush();
            }
            Response.Close();

        }

1 Ответ

0 голосов
/ 08 июля 2018

Нет необходимости делать чанкинг здесь. У вас уже есть двоичные данные, загруженные в память. Просто сделайте один звонок на:

Response.BinaryWrite(binary);

Избегайте создания MemoryStream и еще много чего. У меня такое чувство, что в вашем чанкинге есть ошибка.

Если вы хотите уменьшить использование памяти в вашем приложении при потоковой передаче двоичных данных, вам следует использовать CommandBehavior.SequentialAccess.

Наконец, я предпочитаю обрабатывать подобные вещи, используя простой System.Web.IHttpHandler (ASHX) вместо System.Web.UI.Page (ASPX).

Попробуйте создать файл с именем HectorsHandler.ashx со следующим содержимым:

<%@ WebHandler Language="C#" Class="HectorsApp.HectorsHandler" %>

using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Web;

namespace HectorsApp
{
    public class HectorsHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext ctxt)
        {
            // Request.QueryString["docid"].ToString(); 
            //string DocumentID = Request.QueryString["DocumentID"].ToString();
            string DocumentID = "9163736c-8064-11e8-ab16-2c44fd826130";
            string SessionId = "91494483-8064-11e8-ab16-2c44fd826130";
            //Connection and Parameters
            SqlParameter param1 = null;
            SqlParameter param2 = null;
            using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ProcessManagerConnectionString"].ToString()))
            {
                SqlCommand cmd = new SqlCommand("sp_getdoc", conn);
                cmd.CommandType = CommandType.StoredProcedure;
                param1 = new SqlParameter("@DocumentID", SqlDbType.NVarChar, 100);
                param2 = new SqlParameter("@SessionId", SqlDbType.NVarChar, 100);
                param1.Direction = ParameterDirection.Input;
                param2.Direction = ParameterDirection.Input;
                param1.Value = DocumentID;
                param2.Value = SessionId;
                cmd.Parameters.Add(param1);
                cmd.Parameters.Add(param2);
                //Open connection and fetch the data with reader
                conn.Open();
                using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
                {
                    if (reader.Read())
                    {
                        //
                        string doctype = reader["Extension"].ToString();
                        string docname = reader["Docname"].ToString();
                        //
                        ctxt.Response.BufferOutput = false;
                        ctxt.Response.Buffer = false;
                        ctxt.Response.ContentType = doctype;
                        ctxt.Response.AddHeader("Content-Disposition", "attachment; filename=" + docname);

                        //Code for streaming the object while writing
                        byte[] buffer = new byte[8040];
                        long dataIndex = 0;
                        while (ctxt.Response.IsClientConnected)
                        {
                            long bytesRead = reader.GetBytes(reader.GetOrdinal("Data"), dataIndex, buffer, 0, buffer.Length);
                            if (bytesRead == 0)
                            {
                                break;
                            }
                            ctxt.Response.OutputStream.Write(buffer, 0, (int)bytesRead);
                            ctxt.Response.OutputStream.Flush();
                            dataIndex += bytesRead;
                        }
                    }
                }
            }
        }

        public bool IsReusable
        {
            get { return false; }
        }
    }
}

Согласно комментариям ниже, больше нет необходимости очищать какие-либо данные, так как теперь у вас есть полный контроль над тем, что выводится.

Обратите внимание, что поскольку используется CommandBehavior.SequentialAccess, столбцы должны читаться по порядку.

Есть ли какой-либо выигрыш в производительности от CommandBehavior.SequentialAccess?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...