Para un proyecto asp.Net MVC, necesitaré manejar archivos grandes (principalmente 200-300Mo, a veces 1Go).Aplicación en capas: Almacene el archivo en filestream en la base de datos
Los almacenaré en la base de datos (por razones de seguridad/razón de coherencia).
Estoy preocupado por el problema de rendimiento, por lo que quiero evitar todo lo que pueda tener una matriz de bytes en cualquier parte del programa, el objetivo es trabajar con la transmisión en todas partes.
Tengo una aplicación en capas, que en su mayoría significa que tengo varios "DataStore", que son responsables de conectar y recuperar/insertar/actualizar los datos de la base de datos.
Dado que EF no es compatible con Filestream por ahora, estoy manejando la "parte del archivo" a través de simples solicitudes Sql. He leído un buen artículo sobre el uso filestream aquí: http://blog.tallan.com/2011/08/22/using-sqlfilestream-with-c-to-access-sql-server-filestream-data/
Y tengo algunas preguntas adicionales, que espero que me puedan ayudar/señalarme a la buena dirección:
- desde que he una aplicación en capas, una vez que haya instanciado mi objeto SQLFileStream, ¿podría disponer de SqlCommand/Sql Connection/Transaction scope?
- Si no, ¿cómo se supone que debo cerrarlos?
- En el enlace anterior, hay un ejemplo que muestra cómo usarlo con ASP. Pero dado que estoy usando ASP.Net MVC, ¿no hay un ayudante que pueda transmitir directamente un archivo al navegador? Porque encontré muchos ejemplos de devolución de datos binarios al navegador, pero por ahora, todos los ejemplos que encontré hacen básicamente algo como
Stream.ToArray()
para llenar una matriz de bytes y devolverlos al navegador. Descubrí que puedo devolver unFileStreamResult
que puede incluir el parámetro aStream
. ¿Es esa la dirección correcta?
(No estoy preocupado actualmente por la posibilidad de subir archivos de gran tamaño, ya que se insertan por una pesada cliente en la base de datos)
EDITAR
(Lo siento por el código sucio, es sólo para no tener 50 métodos diferentes aquí. He intentado un poco más, y actualmente estoy atascado con la parte "leer", debido a la parte separada (donde generamos la capa y donde la consumimos):
SqlConnection conn = GetConnection();
conn.Open();
SqlCommand cmd = new SqlCommand(_selectMetaDataRequest, conn);
cmd.Parameters.Add(_idFile, SqlDbType.Int).Value = idFile;
SqlDataReader rdr = cmd.ExecuteReader();
rdr.Read();
string serverPath = rdr.GetSqlString(0).Value;
byte[] serverTxn = rdr.GetSqlBinary(1).Value;
rdr.Close();
return new SqlFileStream(serverPath, serverTxn, FileAccess.Read);
Pero recibo una excepción en rdr.GetSqlBinary(1).Value
porque GET_FILESTREAM_TRANSACTION_CONTEXT devuelve nulo. Encontré here que esto se debe a la transacción faltante.
Intenté con un "TransactionScope" + su llamada .Complete();
. No cambia nada
Se ha intentado realizar un BEGIN TRANSACTION que observamos en el enlace anterior:
SqlConnection connection = GetConnection(); connection.Open(); SqlCommand cmd = new SqlCommand();
cmd.CommandText = "BEGIN TRANSACTION";
cmd.CommandType = CommandType.Text;
cmd.Connection = connection;
cmd.ExecuteNonQuery();
cmd = new SqlCommand(_selectMetaDataRequest, connection);
cmd.Parameters.Add(_idFile, SqlDbType.Int).Value = idFile;
SqlDataReader rdr = cmd.ExecuteReader();
rdr.Read();
string serverPath = rdr.GetSqlString(0).Value;
byte[] serverTxn = rdr.GetSqlBinary(1).Value;
rdr.Close();
SqlFileStream sqlFileStream = new SqlFileStream(serverPath, serverTxn, FileAccess.Read);
cmd = new SqlCommand(); cmd.CommandText = "COMMIT TRANSACTION"; cmd.CommandType = CommandType.Text; cmd.Connection = connection; cmd.ExecuteNonQuery();
Pero se estrella en el primer "ExecuteNonQuery" con el excepción "A transaction that was started in a MARS batch is still active at the end of the batch. The transaction is rolled back."
¡Pero es la PRIMERA consulta ejecutada!
Yo diría que esta es una tarea más adecuada para los sistemas de archivos, ya que se trata de archivos. Suponiendo que el sitio/base de datos se implementa en una máquina de Windows, una alternativa para mirar podría ser Transactional NTFS. Incluso debería integrarse con otras transacciones utilizando el DTM. Es cierto que hay algunos gotcha allí (por ejemplo, mal soporte de archivos compartidos). +1 por no usar byte [] :) –