Guardar ficheros binarios en SQL Server

Published 211 weeks, 3 days ago
Mon Feb 20 2006
En mi proyecto actual, una aplicación ASP .NET, el cliente expresó el deseo de guardar en base de datos las cartas, basadas en plantillas Word y generadas mediante la aplicación. Quieren guardarlas en la base de datos porque, al ser documentos oficiales, tienen que poder volver a imprimirse o visualizarse exactamente igual a como se crearon en su día. Por lo tanto, necesitamos un método para guardar los ficheros Word en la base de datos en formato binario, como stream de datos y ser capaces de poder recuperarlos después para su visualización.

Nota: no es el objetivo de este post la creación de ficheros Word desde código C#. Podéis usar para ello la automatización Word, o componentes de terceros como los publicados por Aspose, que son excelentes.

Vamos a crear una tabla simple en SQL Server para alojar los documentos. Para este ejemplo, la llamaremos DocsBinarios, y tendrá la siguiente estructura:

Campo Tipo Nulos?
DocId Int (identity) No
Documento Image No
NombreDoc VarChar(100) No


Es bastante autoexplicativo: el campo DocId es un campo de clave primaria autogenerado. El campo Documento es el que va a almacenar los streams de bits, es decir los propios ficheros Word en formato binario. El campo NombreDoc almacenará el nombre que se proporcionó originalmente al documento cuando se generó.

Después crearemos un procedimiento almacenado, UploadDocs, que servirá para guardar registros en esta tabla:

CREATE PROCEDURE UploadDoc(@doc AS Image, @nombre AS VarChar(100)) 
AS
INSERT INTO 
DocsBinarios (Documento, NombreDoc) values (@doc, @nombre)
GO

Colorized by: CarlosAg.CodeColorizer

También es bastante autoexplicativo: un simple INSERT INTO que recibe como parámetros un Image y un VarChar

Y éste es el método que guarda los datos en la base de datos:

private void GuardarFicheroBDD(string sRuta, string sFichero)
{
    
//Creamos un nuevo objeto de tipo FileStream para leer el fichero
    //Word en modo binario
    
System.IO.FileStream  fs = new FileStream(sRuta + sFichero,
        System.IO.FileMode.Open)
;
    
//Creamos un array de bytes para almacenar los datos leídos por fs.
    
Byte[] data = new byte[fs.Length];
    
//Y guardamos los datos en el array data
    
fs.Read(data, 0, Convert.ToInt32(fs.Length));
    
//Abrimos una conexion. En este caso los datos de la cadena de
    //conexion a la base de datos se recuperan de una sección del
    //fichero web.config mediante ConfigurationSettings
    
SqlConnection cnn =
       new 
SqlConnection(ConfigurationSettings.AppSettings["conexionBD"]);
    
cnn.Open();
    
//Creamos un comando de tipo StoredProcedure para invocar a
    //UploadDocs
    
SqlCommand cmd = new SqlCommand("UploadDoc", cnn);
    
cmd.CommandType CommandType.StoredProcedure;
   
//Añadimos los parametros esperados y los valores de los mismos
    
cmd.Parameters.Add("@doc", data)//los datos del fichero Word
    
cmd.Parameters.Add("@nombre", sFichero)//y su nombre
    //Ejecutamos el procedimiento almacenado, que inserta un nuevo
    //registro en DocsBinarios con los datos que queremos introducir
    
cmd.ExecuteNonQuery();
    
//Cerramos la conexión y el fichero 
    
cnn.Close();
    
fs.Close();
}

Colorized by: CarlosAg.CodeColorizer

Ahora veamos el método para recuperar esos datos de la base de datos y mostrarlos como un fichero Word:

private void LeerDeBD()
{
    
//Abrimos la conexion, exactamente igual que antes
    
SqlConnection cnn =
        new 
SqlConnection(ConfigurationSettings.AppSettings["conexionBD"]);
    
cnn.Open();
    
//Este es el comando que abre el registro que deseamos. Para este
    //ejemplo abrimos siempre el primer registro, habría que modificar
    // este código para que el método recibiera como parámetro el
    //registro que queremos abrir, claro.
    
SqlCommand comm = new SqlCommand("SELECT * FROM DocsBinarios " +
        " WHERE docId = 1"
, cnn);
    
comm.CommandType CommandType.Text;
    
SqlDataAdapter da = new SqlDataAdapter(comm);
    
DataSet ds = new DataSet("Binarios");
    
da.Fill(ds);
    
//Creamos un array de bytes que contiene los bytes almacenados
    //en el campo Documento de la tabla
    
byte[] bits ((byte[])(ds.Tables[0].Rows[0].ItemArray[1]));            
    
cnn.Close();
    
//Vamos a guardar ese array de bytes como un fichero en el
    //disco duro, un fichero temporal que después se podrá descartar.
    //Para evitar problemas de concurrencia de usuarios,
    //generamos un nombre único para el mismo
    
string sFile "tmp" + GenerarNombreFichero() + ".doc";
    
//Creamos un nuevo FileStream, que esta vez servirá para
    //crear un fichero con el nombre especificado
    
FileStream fs = new FileStream(Server.MapPath(".") +
        @"\DocsGenerados\" 
+ sFile, FileMode.Create);
    
//Y escribimos en disco el array de bytes que conforman
    //el fichero Word
    
fs.Write(bits, 0, Convert.ToInt32(bits.Length));
    
fs.Close();
    
//Para mostrar el fichero, utilizamos una función
    //JavaScript llamada mostrarFichero (que lo único que
    //hace es cargar el fichero especificado)
    //y hacemos que se ejecute en un pop-up.
    
string script "<script languaje='javascript'> ";
    
script +"mostrarFichero('DocsGenerados/" + sFile + "') ";
    
script +"</script>" + Environment.NewLine;
    
Page.RegisterStartupScript("mostrarFichero",script);
}

Colorized by: CarlosAg.CodeColorizer

Y listo. Mediante éste último método el fichero Word se carga desde la base de datos y se muestra en un popup y en todo su esplendor.

Para que tengáis el código completo, ésta es la función JavaScript que carga el fichero generado desde base de datos:

<script language="javascript">
function mostrarFichero(destino) {
window.open(destino,null,"directories=no,height=600,
    width=800,left=0,top=0,location=no,menubar=yes,
    status=no,toolbar=yes,resizable=yes"
)
document.forms(0).submit();
}
</script>

Colorized by: CarlosAg.CodeColorizer

Y éste el método GenerarNombreFichero(), que utiliza la cuenta de ticks del servidor para crear nombres cuasi únicos de fichero.

private string GenerarNombreFichero()
{
    
int ultimoTick 0;
    while
(ultimoTick==Environment.TickCount) 
    {
        System.Threading.Thread.Sleep(
1);
    
}
    ultimoTick
=Environment.TickCount;
    return 
DateTime.Now.ToString("yyyyMMddhhmmss") + "." +
        ultimoTick.ToString()
;
}

Colorized by: CarlosAg.CodeColorizer

Diseño, Personas, y otras cosas

Published 213 weeks, 50 minutes ago
Thu Feb 09 2006
Bueno, pues esto ya va pareciendo un blog en condiciones. Añadidos los vínculos a los blogs técnicos que leo habitualmente, añadida una página de Acerca de..., añadidos algunos otros vínculos de interés general, añadido el nuevo estándar de icono de sindicación, para algo en lo que IE y FF se van a poner de acuerdo...

Hoy ha sido un día activo en cuanto a lecturas. Por un lado, a pesar de que ya conocía a Mort, me entero de que también existen Elvis, Einstein y ahora Hugo. Pues sí que estamos bien.

Decidido definitivamente a dejar de ser Mort o hacerme granjero en el intento, qué casualidad que Scott Reynolds ha comenzado a publicar una serie de artículos básicos sobre OOP. Le he pedido permiso para traducirlo al español, y me lo ha concedido muy amablemente.

Próximamente, aquí.

Unleash it

Published 213 weeks, 15 hours ago
Thu Feb 09 2006
La mayoría de las veces las herramientas más simples son las más adecuadas. Es el caso de Unleash it, antes conocida como Web Deploy, que lo que hace es exactamente eso: copiar ficheros y carpetas a un servidor remoto para facilitarnos el trabajo de subir al servidor una nueva versión de nuestra aplicación Web.

El uso de la aplicación es francamente sencillo: se trabaja mediante perfiles de instalación, o deployment profiles. Lo principal a definir en cada perfil son las rutas, tanto la ruta de origen (presumiblemente la ruta donde tengamos nuestra versión local de desarrollo del sitio web) como la ruta de destino, que será la ruta del servidor donde alojamos dicha web.



Como podéis ver por la captura, podemos también seleccionar un proyecto de Visual Studio como origen, y un perfil FTP o un fichero ZIP como destino. aquí vendría uno de los peros del programa: sólo se puede elegir proyectos de Visual Studio, no soluciones. En la lista de la derecha marcamos los ficheros que queremos incluir en la subida y los directorios que queremos excluir de la subida y listo. Esta sería la configuración básica.

Por supuesto, podemos complicar las cosas. Pinchando en la opción de configuración de perfil, accedemos a este cuadro de diálogo:



en el que podemos realizar varias acciones para afinar nuestro perfil. Entre otras:
  • Mostrar un cuadro de diálogo en cada deployment para especificar exactamente qué ficheros y qué carpetas queremos subir al servidor. Muy útil cuando sólo queremos subir algo en concreto, pero su uso continuado invalidaría el objetivo principal de esta herramienta, que es precisamente el poder subir una solución web con un sólo click.
  • Guardar un log de las acciones realizadas por el perfil.
  • Comandos de pre y post deployment. En mi caso concreto voy a tener que usar esto: resulta que uno de los ficheros que en mi caso concreto debo ignorar al subir la aplicación es el web.config, ya que las opciones de configuración en local y en servidor son distintas y no quiero machacarlo. Sin embargo, en el web.config se guardan también las opciones de navegación de UIProcess que utiliza la aplicación, y ésas de momento tengo que acutalizarlas a mano.
  • Posibilidad de hacer backup
  • Uso de plugins. Una de las opciones más interesantes, ya que nos permite el uso de aplicaciones externas desde Unleash It. Los plugins que vienen incluidos con la aplicación son una conexión con NAnt, la posibilidad de conectarse previamente con Visual SourceSafe o con Vault, de Sourcegear, y la posibilidad de excluir del deployment ficheros concretos por nombre, en lugar de por máscara.


Por último, podemos combinar varios perfiles de deployment y usarlos juntos en una sola acción mediante la solapa Multiple Profile Deployment, como se puede ver:



En resumidas cuentas, existen herramientas más potentes y complejas, como también existen proyectos más complicados y con mayores necesidades que en el que trabajo actualmente. Para mis necesidades actuales, Unleash It es una herramienta perfecta, que me permite subir una nueva versión de mi aplicación al servidor con un sólo click. Y para eso sirven las herramientas, para ganar productividad.

IE7 Beta 2

Published 214 weeks, 13 hours ago
Thu Feb 02 2006
Anoche pude por fin (es lo que tiene no tener suscripción a MSDN, que te toca esperar) instalarme en casa la Beta 2 Preview de IE7. Si vais a descargarla y probarla, dos precauciones a tener en cuenta: el sistema operativo tiene que ser Windows XP SP2 o Windows 2003, no funciona en Windows 2000. La segunda, que requiere que vuestro Windows pase el filtro de la validación de software original de Microsoft. Y no, esta vez no se puede ignorar el chequeo y continuar con la instalación.

Me ha gustado, la verdad. Mucho. No lo he probado en exceso, nada más que mi navegación de todos los días cuando llego a casa; a saber, Bloglines, GMail, los links interesantes que me hallan llegado por correo y poco más. Lo voy a probar mucho más en próximos días, porque ya ha destronado a Maxthon como mi navegador predeterminado, así que si veo algo interesante lo contaré por aquí, sin duda. De momento los hard-core users de Internet Explorer tendremos que acostumbrarnos a que Mayúsculas + Click abre un vínculo en una ventana nueva y Control + Click en una pestaña nueva.

Y una cosa curiosa: tenía almacenados en Favoritos precisamente esta dirección de Internet, www.picacodigos.com; pero la tenía guardada sin el http://. Pues IE7 no lo reconocía como un vínculo válido precisamente por eso.

Actualización 08/02/2006: Es muy beta. Me gusta, es funcional y está muy bien diseñado y cuando sea un producto terminado lo usaré sin lugar a dudas... pero por ahora me ha dado demasiados errores. De vuelta a Firefox. Sigh.

Para desinstalarlo, por si acaso encontráis algún problemilla, usad Panel de Control -> Agregar o Quitar Programas, pero debéis aseguraros de tener marcada la casilla Mostrar Actualizaciones. Estará al final de la lista, justo debajo de todos los Service Packs y parches de Windows XP. Lo desinstaláis, reiniciáis la máquina y listo.