Constrúyete tu propio gestor documental

Uno de los programas más utilizados en nuestro sistema de gestión es el gestor documental, como todos sabéis Sharepoint se conforma entre otras cosas como un gestor de contenidos, que permite almacenar todo tipo de documentos, pero hay veces que buscamos una mayor integración en nuestros sistemas o que simplemente no queremos depender de Sharepoint. En estos casos, desarrollar un gestor documental puede ser una buena alternativa y el trabajo para construir este programa lejos de parecer complicado es relativamente sencillo.

El gestor que os propongo se basa en el mismo sistema que utiliza sharepoint, los ficheros se serializan, se encriptan si es necesario y finalmente se guardan en una tabla de una base de datos tal y como hace sharepoint, posteriormente se leen desde la base de datos y se guardan en una ruta local o se utilizan directamente, aprovechándonos del almacenamiento en la base de datos con todas las ventajas que esto conlleva, copias de seguridad, particionado de tablas, indexación de contenidos, etc.

El gestor que vamos a desarrollar debe tener capacidad para almacenar cualquier tipo de fichero, documento Word, hojas de cálculo, archivos pdf, archivos comprimidos, emails, jpgs, archivos de autocad, etc.

Para comenzar hemos creado una tabla llamada Documentación en una base de datos independiente, porque como supondréis esta puede llegar a ser muy grande y en un momento determinado quizás tengamos que alojarla o particionarla en otro servidor por escalabilidad,  tamaño o rendimiento.

clip_image002

El campo [documento] (varbinary(max)) será el encargado de almacenar los ficheros serializados.

Buscamos un método sencillo para poder subir los archivos a la base de datos, con lo que queremos arrastrar uno o varios ficheros a un contenedor para que la aplicación pueda traspasarlos posteriormente a la BD.

Para llevar a cabo esta operación, hemos creado un formulario con el control System.Windows.Forms.WebBrowser que hace referencia a una ruta temporal en el disco, en este caso hemos mapeado una unidad temporal en nuestro PC ‘c:temporal’, aunque esta podria ser tambien una ruta compartida de red. La configuración de esta carpeta se realiza a través de la propiedad “Path” del control WebBrowser. Este control ya tiene por defecto implementados el sistema Drag & Drop, que nos permite arrastrar, copiar o pegar cualquier tipo de archivo de sistemas Windows, así podemos arrastrar varios correos desde Outlook, una hoja de cálculo de Excel o cualquier fichero simplemente utilizando el ratón, al realizar esta operación sobre los archivos seleccionados estos se mueven o se copian a la ruta temporal que hemos establecido, podemos además utilizar los atajos para copiar archivos Ctrl-c / Ctrl-v, etc.

El control se puede configurar para que muestre diferentes vistas, hemos configurado una que muestra el nombre de los archivos, su tipo, la fecha, el tamaño, esta vista se puede alterar cambiando las propiedades de la carpeta temporal.

Esta primera parte que parecía complicada queda resuelta facilmente sin apenas tener que escribir un par de lineas de código, correspondientes a la configuración del control Webbrowser.

clip_image004

Una vez hemos arrastrado los archivos y estos se copian automáticamente a la ruta temporal pasamos al proceso de verificación, en esta opción se nos permite asociar cada uno de los archivos a varios metadatos, de esta forma podemos añadir información adicional a cada elemento como una descripción más detallada, observaciones, usuario, fecha o cualquier información que nos parezca relevante.

clip_image006

Finalmente los ficheros se procesan uno a uno y se van traspasando a la base de datos, para realizar esta operación podéis utilizar Directory.GetFiles(path) que permite obtener los nombres de los archivos de una ubicación determinada y FileInfo(file) que permite obtener información detallada sobre cada uno de los ficheros, fecha de creación, tamaño, etc. No debemos olvidar guardar el nombre completo con la extensión para identificar su tipo cuando vayamos a utilizarlo. 

Con todos estos datos formados por los nombres de los ficheros, sus atributos y los metadatos que podemos variar conformamos una colección temporal, lógicamente estos se grabaran posteriormente en la base de datos con lo que tendrán que tener un campo asociado en la tabla que creamos inicialmente.

Para realizar las operaciones de serialización de un fichero utilizamos la siguiente función:

//Cargar en un array de bytes el documento.
using (FileStream archivoStream = new FileStream(file, FileMode.Open, FileAccess.Read))
{
    byte[] documento = new byte[(int) archivoStream.Length];
    archivoStream.Read(documento, 0, (int) archivoStream.Length);
}

Despues de realizar este paso, si deseamos encriptar el documento para mayor seguridad podriamos hacerlo en este punto, utilizando las librerias de .net o aprovechando Sql Server para realizar esta operación.

Posteriormente grabamos el archivo y todos los datos adjuntos al documento (Metadata) utilizando un Procedimiento almacenado.

cmdOrden.Parameters.Add("@documento", SqlDbType.VarBinary).Value = documento.Archivo;
cmdOrden.Parameters.Add("@descripcion_modulo", SqlDbType.NVarChar, 100).Value = documento.Descripcion_modulo;
cmdOrden.Parameters.Add("@descripcion", SqlDbType.NVarChar, 200).Value = documento.Descripcion;
cmdOrden.Parameters.Add("@ruta", SqlDbType.NVarChar, 200).Value = documento.Ruta;
cmdOrden.Parameters.Add("@archivo", SqlDbType.NVarChar, 200).Value = documento.Archivo;
cmdOrden.Parameters.Add("@modulo", SqlDbType.NVarChar, 50).Value = documento.Modulo;
cmdOrden.Parameters.Add("@codigoAuxiliar", SqlDbType.Int, 10).Value = documento.Codigo_auxiliar;
cmdOrden.Parameters.Add("@codigoAsociado", SqlDbType.Int, 10).Value = documento.Codigo_asociado;
cmdOrden.Parameters.Add("@usuario", SqlDbType.NChar, 5).Value = documento.Usuario;
cmdOrden.Parameters.Add("@tipo", SqlDbType.NVarChar, 100).Value = documento.Tipo;
cmdOrden.Parameters.Add("@observaciones", SqlDbType.NVarChar, 1000).Value = documento.Observaciones;
cmdOrden.Parameters.Add("@codigo", SqlDbType.Int, 10).Direction = ParameterDirection.Output;

De esta forma tan sencilla los documentos se traspasan a la base de datos.

Pensando que este proceso puede ser de varios ficheros y que además algunos pueden tener un tamaño considerable, hemos dotado al programa de un mecanismo asíncrono utilizando BackGroundWorked, de forma que si el proceso tarda un tiempo la aplicación nunca llegue a bloquearse y los usuarios puedan continuar realizando su trabajo mientras se trasladan los ficheros a la BD.

Una vez hemos procesado los ficheros, lo unicó que nos queda por hacer es explotarlos, para ello tenemos que realizar la operación inversa:

Creamos otro formulario, en nuestro caso hemos utilizado un control TreeView para poder visualizar los datos de forma jerarquica, habitualmente los documentos estan relacionados con un registro de una o varias tablas, en la foto de abajo se muestran varios registros relacionados con la ficha de un artículo. Si el usuario pulsa abrir, se lee el registro de la base de datos, se deserializa el contenido del campo [documento], se desencripta si fuera necesario y finalmente se guarda en una ruta temporal o se abre directamente en memoria. Para deserializar el documento utilizamos la siguiente función, donde [doc.Documento] hace referencia al campo de tipo byte[] de nuestra entidad y [fichero] a la ruta y nombre del archivo que se va a crear como por ejemplo ‘c:tempestadisticas.xls’.

using (FileStream archivoStream = new FileStream(fichero, FileMode.Create))
{
    archivoStream.Write(doc.Documento, 0, doc.Documento.Length);
    archivoStream.Close();
}

Finalmente abrimos el fichero, para realizar esta operación y que el programa relacionado (word, excel, outlook), realice esta operación automáticamente utilizamos la siguiente función.

if (File.Exists(fichero))
{
    Process process = new Process { StartInfo = { FileName = fichero } };
    process.Start();
}

 

clip_image008

Y voila, ya tenemos nuestro gestor documental. En nuestro caso para complementar la utilidad vamos a llamar a un webservice que nos permite almacenar el fichero también en sharepoint de manera que este accesible en los dos gestores, podemos además implementar filtros para realizar búsquedas no solo por los metadatos si no por el contenido de los ficheros, implementar un control de versiones, etc, etc., pero si continuamos al final lo que tendríamos es otro “Sharepoint”.

La ventaja de esta utilidad además de la integración con nuestro sistema y la facilidad de uso es que podemos asociar el contenido a nuestros registros de la base de datos y posteriormente visualizarlos o agruparlos en otro registro para facilitar el acceso a la información.

image

En nuestro caso cada registro de cada mantenimiento tiene accesible el gestor documental de manera que en el ejemplo de la foto posterior cada registro de artículos tiene asociados varios documentos, otra ventaja es que en ciertos mantenimientos podemos visualizar la información de forma jerárquica, por ejemplo en un cliente se verían de esta forma, con lo que acceder a la información es muy sencillo.

clip_image010

Así que animaros, construiros un pequeños gestor documental como este es muy sencillo, y permitira dotar a vuestras aplicaciones de una herramiena muy potente para centralizar y acceder a todo tipo de información.

11 comentarios en “Constrúyete tu propio gestor documental”

  1. Lo siento Juan Pablo, no puedo subir el código ya que utiliza partes de toda la aplicación basado en nuestro propio sistema de entidades, con lo que tendria que subir practicamente toda la aplicación, por eso he escrito el código de las partes mas relevantes para que facilmente puedas construir una utilidad similar

    Un saludo.

  2. Como explico en el post utilizo un bucle similar a este, dentro de un threat diferente utilizando backgroundWorked para no bloquear el programa mientras se cargan los ficheros. El esquema sería similar a este dentro de un bucle for voy leyendo los ficheros con los metadatos y almacenando cada uno de ellos en la base de datos.

    private const string path = @”c:temp”;

    foreach (string file in Directory.GetFiles(path))

    {

         – Recoje los metadatos del archivo utilizando

         FileInfo fileInfo = new FileInfo(file);

         – Serializa el archivo utilizando la función espuesta arriba

         – Encripta el archivo si es necesario

         – Recojo el documento serializado y encriptado con los metadatos de fileInfo y los introducidos por el usuario, se los paso al SP

         – Llama al sp para guardar los datos en la tabla

         – Informa si el archivo se ha subido con éxito

    }

    La información sobre como utilizar BackgroundWorked, encriptar, y utilizar un Store Procedure la puedes encontrar facilmente en la web, por eso solo he puesto las partes mas relevantes, pero en conjunto es un programa muy sencillo.

    Espero que con esta explicación quede más claro.

    Un saludo.

  3. Hola amigo, la verdad que una excelentisima idea, muy útil en mi caso.
    Un par de dudas conceptuales:
    – ¿Cómo sería el control de versiones? Utilizando el hash de cada file que se quiera subir y comparandolo con el que hay almacenado para ver si esta modificado? O te referis a algo como generar “Version 2” manualmente de un archivo y subirlo?
    – ¿Cómo sería una aproximación a la búsqueda en el contenido del archivo? Porque no lo veo muy perfomante tener que ir uno por uno abriendo y buscando un patron en los archivos que estan en la BD.
    Salu2

  4. Hola, excelente planteamiento.

    Estoy siguiendolo pero me pregunto como se podría abrir el documento sin tener que grabarlo en disco, desde memoria.

    Podrías decirme algo?

    Gracias y un saludo

  5. @Pablo, el control de reviones lo podrias hacer como un hash y comparando, aunque una forma mucho mas facil seria crear un trigger en la tabla de manera que cada actualización del registro (INSERT, UPDATE, DELETED) genere un registro en una tabla similar de historicos y guarde la copia del registro, de esta forma con un simple trigger podrias montar el control de versiones, para las busquedas e indexación te aconsejo que leas http://geeks.ms/blogs/vgarcia/archive/2010/05/04/streaming-de-libros-parte-2-indexaci-243-n-con-full-text-search.aspx para tener una pequeña idea de como implementarlo.

    Un saludo.

  6. @Luis, quizas puedas resolverlo con algo así utilizando las librerias para web, aunque no lo he probado. Un saludo.

    System.IO.MemoryStream mstream = GetData();
    //Convert the memorystream to an array of bytes.
    byte[] byteArray = mstream.ToArray();
    //Clean up the memory stream
    mstream.Flush();
    mstream.Close();
    // Clear all content output from the buffer stream
    context.Response.Clear();
    // Add a HTTP header to the output stream that specifies the default filename
    // for the browser’s download dialog
    context.Response.AddHeader(“Content-Disposition”, “attachment; filename=”+context.Request.Form[“txtFileName”].ToString());
    // Add a HTTP header to the output stream that contains the
    // content length(File Size). This lets the browser know how much data is being transfered
    context.Response.AddHeader(“Content-Length”, byteArray.Length.ToString());
    // Set the HTTP MIME type of the output stream
    context.Response.ContentType = “application/octet-stream”;
    // Write the data out to the client.
    context.Response.BinaryWrite(byteArray);

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *