¿Guardar cualquier archivo en la base de datos y simplemente convertirlo en una matriz de bytes?

Resuelto Blankman asked hace 14 años • 8 respuestas

¿Convertir un archivo a una matriz de bytes es la mejor manera de guardar CUALQUIER formato de archivo en el disco o en la columna binaria var de la base de datos?

Entonces, si alguien quiere guardar un archivo .gif o .doc/.docx o .pdf, ¿puedo simplemente convertirlo a un bytearray UFT8 y guardarlo en la base de datos como una secuencia de bytes?

Blankman avatar Apr 05 '10 22:04 Blankman
Aceptado

Como no se menciona a qué base de datos te refieres, supongo que SQL Server. La siguiente solución funciona tanto para 2005 como para 2008.

Tienes que crear una tabla con VARBINARY(MAX)una de las columnas. En mi ejemplo, creé una tabla Raportycon la columna RaportPlikcomo VARBINARY(MAX)columna.

Método para poner fileen la base de datos desdedrive :

public static void databaseFilePut(string varFilePath) {
    byte[] file;
    using (var stream = new FileStream(varFilePath, FileMode.Open, FileAccess.Read)) {
        using (var reader = new BinaryReader(stream)) {
            file = reader.ReadBytes((int) stream.Length);       
        }          
    }
    using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetails))
    using (var sqlWrite = new SqlCommand("INSERT INTO Raporty (RaportPlik) Values(@File)", varConnection)) {
        sqlWrite.Parameters.Add("@File", SqlDbType.VarBinary, file.Length).Value = file;
        sqlWrite.ExecuteNonQuery();
    }
}

Este método consiste en obtener filede la base de datos y guardarla endrive :

public static void databaseFileRead(string varID, string varPathToNewLocation) {
    using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetails))
    using (var sqlQuery = new SqlCommand(@"SELECT [RaportPlik] FROM [dbo].[Raporty] WHERE [RaportID] = @varID", varConnection)) {
        sqlQuery.Parameters.AddWithValue("@varID", varID);
        using (var sqlQueryResult = sqlQuery.ExecuteReader())
            if (sqlQueryResult != null) {
                sqlQueryResult.Read();
                var blob = new Byte[(sqlQueryResult.GetBytes(0, 0, null, 0, int.MaxValue))];
                sqlQueryResult.GetBytes(0, 0, blob, 0, blob.Length);
                using (var fs = new FileStream(varPathToNewLocation, FileMode.Create, FileAccess.Write)) 
                    fs.Write(blob, 0, blob.Length);
            }
    }
}

Este método consiste en obtener filede la base de datos y ponerlo comoMemoryStream :

public static MemoryStream databaseFileRead(string varID) {
    MemoryStream memoryStream = new MemoryStream();
    using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetails))
    using (var sqlQuery = new SqlCommand(@"SELECT [RaportPlik] FROM [dbo].[Raporty] WHERE [RaportID] = @varID", varConnection)) {
        sqlQuery.Parameters.AddWithValue("@varID", varID);
        using (var sqlQueryResult = sqlQuery.ExecuteReader())
            if (sqlQueryResult != null) {
                sqlQueryResult.Read();
                var blob = new Byte[(sqlQueryResult.GetBytes(0, 0, null, 0, int.MaxValue))];
                sqlQueryResult.GetBytes(0, 0, blob, 0, blob.Length);
                //using (var fs = new MemoryStream(memoryStream, FileMode.Create, FileAccess.Write)) {
                memoryStream.Write(blob, 0, blob.Length);
                //}
            }
    }
    return memoryStream;
}

Este método consiste en poner MemoryStreamen la base de datos:

public static int databaseFilePut(MemoryStream fileToPut) {
        int varID = 0;
        byte[] file = fileToPut.ToArray();
        const string preparedCommand = @"
                    INSERT INTO [dbo].[Raporty]
                               ([RaportPlik])
                         VALUES
                               (@File)
                        SELECT [RaportID] FROM [dbo].[Raporty]
            WHERE [RaportID] = SCOPE_IDENTITY()
                    ";
        using (var varConnection = Locale.sqlConnectOneTime(Locale.sqlDataConnectionDetails))
        using (var sqlWrite = new SqlCommand(preparedCommand, varConnection)) {
            sqlWrite.Parameters.Add("@File", SqlDbType.VarBinary, file.Length).Value = file;

            using (var sqlWriteQuery = sqlWrite.ExecuteReader())
                while (sqlWriteQuery != null && sqlWriteQuery.Read()) {
                    varID = sqlWriteQuery["RaportID"] is int ? (int) sqlWriteQuery["RaportID"] : 0;
                }
        }
        return varID;
    }
MadBoy avatar Apr 05 '2010 16:04 MadBoy

Si bien puedes almacenar archivos de esta manera, tiene importantes desventajas:

  • La mayoría de las bases de datos no están optimizadas para cantidades gigantescas de datos binarios y el rendimiento de las consultas a menudo se degrada drásticamente a medida que la tabla se hincha, incluso con índices. (SQL Server 2008, con el tipo de columna FILESTREAM, es la excepción a la regla).
  • La copia de seguridad/replicación de la base de datos se vuelve extremadamente lenta.
  • Es mucho más fácil manejar una unidad dañada con 2 millones de imágenes (simplemente reemplace el disco en el RAID) que una tabla de base de datos que se corrompe.
  • Si elimina accidentalmente una docena de imágenes en un sistema de archivos, sus encargados de operaciones pueden reemplazarlas con bastante facilidad desde una copia de seguridad y, dado que el índice de la tabla es pequeño en comparación, se puede restaurar rápidamente. Si elimina accidentalmente una docena de imágenes en una tabla de base de datos gigante, tendrá una larga y dolorosa espera para restaurar la base de datos desde la copia de seguridad, paralizando todo su sistema mientras tanto.

Estos son sólo algunos de los inconvenientes que se me ocurren. Para proyectos pequeños puede valer la pena almacenar archivos de esta manera, pero si está diseñando software de nivel empresarial, no lo recomendaría enfáticamente.

Dan Story avatar Apr 05 '2010 16:04 Dan Story