C# Извлечь файл из поля varbinary (max) - файл поврежден - PullRequest
0 голосов
/ 06 мая 2020

Проблема: у меня есть база данных, в которой файлы находятся в столбце varbinary (max). Я знаю тип файла, потому что есть поле расширения. Мне нужно вытащить данные и сохранить файлы в файловой системе. У меня не так много информации о том, как хранятся данные. В БД это выглядит так:

enter image description here

Я пробовал несколько разных подходов, чтобы получить это значение и сохранить его как файл. Я могу легко создавать файлы из данных, но они всегда повреждены. Я получаю это сообщение при открытии PDF-файла. enter image description here

Я получаю сообщение аналогичного типа, если это файл Excel. (xls) Я пробовал следующие методы для доступа к данным, первые 2 с использованием считывателя SQL с или без commandBehaviour.sequentialAccess. Третий использует SQL Substring.

byte[] data = (byte[])reader["Data"];

// OR

int ordinal = reader.GetOrdinal("Data");
long bufferSize = reader.GetBytes(ordinal, 0, null, 0, 0);
byte[] outbyte = new byte[bufferSize];
reader.GetBytes(ordinal, 0, outbyte, 0, (int)bufferSize);

// OR

        public System.IO.Stream GetStream(AttachmentModel info)
    {
        var attachment = (AttachmentModel)info;

        int start = 0;
        int packetSize = 65535;
        int length = packetSize;
        byte[] data = new byte[info.length];

        while (start < info.length)
        {
            length = Math.Min(packetSize, ((int)info.length - start));
            byte[] buffer = repo.ReadAttachmentData(attachment.id, start, length);
            buffer.CopyTo(data, start);
            start += length;
        }

        return new MemoryStream(data);
    }
    // ReadAttachmentData method
    public byte[] ReadAttachmentData(Guid attachmentID, int start, int length)
    {
        using (SqlConnection connection = GetOpenSqlConnection())
        {
            using (SqlCommand cmd = connection.CreateCommand())
            {
                byte[] buffer = null;

                cmd.CommandText = @"SELECT SUBSTRING([Data], @start, @length) FROM myBlobTable WHERE [Attachment] = @attachmentID";
                cmd.CommandType = CommandType.Text;
                AddParamWithValue(cmd, "@start", DbType.Int32, start + 1); // index starts at 1
                AddParamWithValue(cmd, "@length", DbType.Int32, length);
                AddParamWithValue(cmd, "@attachmentID", DbType.Guid, attachmentID);
                buffer = (byte[])cmd.ExecuteScalar();

                return buffer;
            }
        }
    }
    private IDataParameter AddParamWithValue(SqlCommand cmd, string name, DbType type, object value)
    {
        IDbDataParameter param = cmd.CreateParameter();
        param.ParameterName = name;
        param.DbType = type;
        param.Value = (value ?? DBNull.Value);
        param.Size = 0;
        param.Direction = ParameterDirection.Input;
        cmd.Parameters.Add(param);
        return param;
    }

Чтобы сохранить данные в файл, я пробовал:

System.IO.File.WriteAllBytes(fileName, byteArray);

// OR

using (Stream st = GetStream(att))
{
    using (FileStream fs = new FileStream(att.filePath + "File1" + att.extension, FileMode.Create, FileAccess.Write))
    {
        st.CopyTo(fs);
        st.Flush();
        fs.Flush();
        st.Close();
        fs.Close();
     }
};

// I tried using the deflate stream thinking maybe it was compressed.

using (Stream st = GetStream(attachment))
using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write))
{
    st.CopyTo(fs);
    st.Flush();
    fs.Flush();
    st.Close();
    fs.Close();
}

Я также пробовал использовать параметры размера буфера в моем FileStream. Я безуспешно пробовал практически все комбинации описанных выше методов доступа к данным и сохранения данных.

Еще одна подсказка - если я открою файл в блокноте, данные будут выглядеть так:

data:; base64, JVBERi0xLjQKJcfsj6IKNSAwIG9iago8PC9MZW5ndGg .... et c, et c.

Любая помощь приветствуется, спасибо!

1 Ответ

1 голос
/ 06 мая 2020

Разобрался - в основном методом проб и ошибок. Основываясь на данных, которые я нашел в файле при открытии в блокноте, я извлекаю строку base64 и конвертирую ее в файл. Итак, мой процесс заключался в том, чтобы получить байты из БД, извлечь строку base64, преобразовать обратно в байты, записать файл.

Доступ к файлу varbinary (max) следующим образом:

using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess))
{
    List<AttachmentModel> attachments = new List<AttachmentModel>();
    while (reader.Read())
    {
        AttachmentModel att = new AttachmentModel();
        int ordinal = reader.GetOrdinal("Data");
        long bufferSize = reader.GetBytes(ordinal, 0, null, 0, 0);
        byte[] outbyte = new byte[bufferSize];
        reader.GetBytes(ordinal, 0, outbyte, 0, (int)bufferSize);
        att.data = outbyte; 

        attachments.Add(att);
    }

    return attachments;
}

И конвертируем в файл так:

    using (MemoryStream ms = new MemoryStream(att.data)) {
    string fileContents;
    using (StreamReader reader = new StreamReader(ms))
    {
        fileContents = reader.ReadToEnd();
        List<string> arr = fileContents.Split(',').ToList<string>();

        // convert base 64 string back to bytes
        var myBytes = Convert.FromBase64String(arr[1]);
        System.IO.File.WriteAllBytes(att.filePath + "File1" + att.extension, myBytes);
     }
}
...