Documentos de Académico
Documentos de Profesional
Documentos de Cultura
MSDN Magazine > Inicio > Todos los números > 2006 > November > Basic Instincts: Generación de documentos ...
Basic Instincts
Generación de documentos de
Word 2007 en el servidor
Ted Pattison
Hasta ahora, la escritura e implementación de aplicaciones de servidor que leen, modifican y generan
documentos de Microsoft® Office presentaba un reto. El antiguo formato binario usado por
Microsoft Word, Excel® y PowerPoint® se presentó en 1997 y se ha mantenido como el formato de
archivo predeterminado hasta la versión de Office 2003. Sin embargo, se ha comprobado que resulta
demasiado complicado trabajar con este formato de archivo binario. La gran mayoría de aplicaciones de
producción que leen y escriben documentos de Office lo hacen a través del modelo de objeto de la
aplicación de Office de alojamiento.
Las aplicaciones y los componentes que usan el modelo de objeto de aplicaciones como Word o Excel,
se ejecutan mucho mejor en el escritorio que en escenarios de servidor. Cualquiera que haya dedicado
tiempo a escribir código de infraestructura adicional necesario para que una aplicación de escritorio de
Office se comporte de forma confiable en el servidor, comentará que es una solución nada deseable.
Esto se debe a que las aplicaciones de escritorio de Office como Word y Excel no se diseñaron para
ejecutarse en un servidor y requieren un programa de utilidad personalizado para cerrarse y reiniciarse
si se encuentran con un diálogo modal que precise de la intervención humana. MSDN Magazine Blog
La capacidad de leer y escribir documentos de Office sin contar con el modelo de objeto de la aplicación
de Office de alojamiento resulta mucho más deseable en un escenario de servidor. Office 2000 y November Issue of MSDN Magazine
Office 2003 presentaban algunas capacidades sencillas de creación de libros de Excel y documentos de As I wrote earlier, the Government Special Issue
Word mediante XML. Este avance ofrecía la posibilidad de escribir fragmentos de un documento de of MSDN Magazine, currently live on our Web
Office con una biblioteca XML como la que incluye Microsoft .NET Framework mediante el espacio de site, is worth checking out. The articles in the
nombres System.Xml. special... More...
martes, nov. 5
En el sistema Microsoft Office 2007, Microsoft ha llevado esta idea mucho más lejos adoptando los
formatos de archivo XML abiertos de Office para los documentos usados por Microsoft Office Word, Government Special Issue of MSDN Magazine
Excel y PowerPoint 2007. Estos formatos han entusiasmado a los desarrolladores de ASP.NET y Visit the MSDN Magazine Web site and you’ll
SharePoint®, ya que les ofrecen la posibilidad de leer, escribir y generar documentos de Word, libros de see we’ve been a bit busier than usual of late. In
Excel o presentaciones de PowerPoint en el servidor sin necesidad de que el sistema ejecute una addition to the regularly scheduled November
aplicación de escritorio de Office en el mismo. issue, fea... More...
viernes, nov. 1
Los formatos de archivo XML abiertos de Office van camino de convertirse en un estándar publicado por
la Asociación europea de fabricantes de informática (ECMA). Puede consultar más información sobre el More MSDN Magazine Blog entries >
proceso de normalización actual, descargar el último borrador de especificaciones de los formatos de
archivo XML abiertos de Office y buscar otros fantásticos recursos en línea en openxmldeveloper.com. El
objetivo del artículo de este mes es presentar los datos de programación necesarios para crear sencillos
documentos de Word en una aplicación ASP.NET y devolverlos a usuarios que podrán abrirlos
directamente con Word 2007. Current Issue
figura 1 archivo DOCX es un archivo ZIP (Haga clic aquí para obtener una imagen más grande) (Hacer clic
en la imagen para ampliarla)
El archivo del nivel superior (Hello.docx) se conoce como paquete. Puesto que un paquete se implementa
como un archivo ZIP estándar, ofrece una compresión automática y hace que su contenido esté accesible
de inmediato para muchas utilidades y API existentes, tanto en plataformas Windows como en otras
plataformas similares.
Dentro de un paquete hay dos tipos de contenido interno: componentes y elementos. En general, los
componentes incluyen el contenido y los elementos contienen los metadatos que describen los
componentes. Los elementos se pueden dividir aún más en elementos de relación y elementos de tipo de
contenido. Un componente es una parte interna que incluye contenido que se almacena en el paquete. La
mayoría de los componentes son archivos de texto simples que se serializan como XML con un esquema
XML asociado. No obstante, los componentes también se pueden serializar como datos binarios si es
necesario, por ejemplo, cuando un documento de Word contiene una imagen o un archivo multimedia.
Los nombres de los componentes son identificadores uniformes de recursos (URI) que contienen la ruta
relativa dentro del archivo de paquete junto con el nombre del archivo del componente. Por ejemplo, la
parte principal de un paquete de un documento de Word es /word/document.xml. A continuación, se
incluyen algunos nombres de componente típicos adicionales que encontrará en el paquete de un
documento de Word simple:
/[Content_Types].xml
/_rels/.rels
/docProps/app.xml
/docProps/core.xml
/word/_rels/document.xml.rels
/word/document.xml
/word/fontTable.xml
/word/settings.xml
/word/styles.xml
/word/theme/theme1.xml
Los formatos de archivo XML abiertos de Office usan relaciones para definir las asociaciones entre un
componente de origen y uno de destino. La relación de un paquete define una asociación entre el
paquete de nivel superior y un componente. La relación de un componente define una asociación entre
un componente principal y uno secundario. Las relaciones son importantes porque permiten detectar
estas asociaciones sin tener que examinar el contenido de los componentes en cuestión. Esto significa
que las relaciones son independientes del esquema específico de contenido y, por tanto, permiten una
resolución más rápida. Existe una ventaja adicional por el hecho de poder establecer una relación entre
dos componentes sin tener que modificar cada uno de ellos.
Las relaciones se definen en las partes internas conocidas como elementos de relación. Un elemento de
relación se almacena en el interior de un paquete al igual que un componente, aunque no se considera
componente como tal. Para mantener la coherencia, los elementos de relación se crean siempre dentro
de las carpetas _rels.
Por ejemplo, un paquete contiene exactamente un elemento de relación de paquete denominado
/_rels/.rels. El elemento de relación de paquete contiene elementos XML para definir las relaciones del
paquete, como, por ejemplo, la que existe entre el paquete del nivel superior para un archivo .docx y el
componente interno /word/document.xml, como se muestra a continuación:
Como puede ver, un elemento de relación define un nombre, un tipo y un componente de destino.
También observará que el nombre de tipo de la relación se define con las mismas convenciones que se
usan para crear espacios de nombres XML.
Además del elemento de relación de paquete, los paquetes pueden contener también uno o varios
elementos de relación de componente. Por ejemplo, las relaciones entre /word/document.xml y los
componentes secundarios se definen en un elemento de relación de paquete ubicado en el URI
/word/_rels/document.xml.rels. Tenga en cuenta que el atributo Target de una relación del elemento de
relación de componente es un URI relativo al componente principal y no al paquete de nivel superior.
Cada componente de un paquete se define en cuanto a un tipo de contenido específico. Los tipos de
contenido son metadatos que definen un tipo de medio, un subtipo y un conjunto de parámetros
opcionales del componente. Cualquier tipo de contenido que se use en un paquete, se debe definir de
forma explícita en un elemento de tipo de contenido. Cada paquete cuenta con un elemento de tipo de
contenido denominado /[Content_Types].xml. En la figura 2, se muestra un ejemplo de definiciones de
tipo de contenido incluidas en /[Content_Types].xml. Observe en la ilustración que los elementos de
relación son como componentes, ya que también se definen con un tipo de contenido asociado.
"http://schemas.openxmlformats.org/package/2006/content-types">
<Override PartName="/word/document.xml"
ContentType="application/vnd.openxmlformats-
officedocument.wordprocessingml.document.main+xml"/>
</Types>
El usuario de un paquete usa los tipos de contenido para interpretar cómo leer y representar el
contenido de los componentes. Como puede observar en la figura 2, un tipo de contenido
predeterminado normalmente está asociado a una extensión de archivo, como .rels o .xml. Los tipos de
contenido de invalidación se usan para definir un tipo de contenido que es distinto al predeterminado y
que está asociado a la extensión de archivo en un componente específico. Por ejemplo, la figura 2
muestra cómo /word/document.xml está asociado a un tipo de contenido que es distinto del tipo de
contenido predeterminado que se usa para archivos con la extensión .xml.
figura 3 agregue referencia a WindowsBase.dll (Haga clic aquí para obtener una imagen más
figura 3 agregue referencia a WindowsBase.dll (Haga clic aquí para obtener una imagen más
grande) (Hacer clic en la imagen para ampliarla)
Las clases que componen la API de paquete están incluidas en el espacio de nombres System.IO.Package.
Al trabajar con paquetes, con frecuencia programará con clases antiguas y (esperemos que) conocidas
en los espacios de nombres System.IO y System.Xml. Examine el código de la figura 4 que muestra el
esqueleto de creación de un nuevo paquete.
Imports System
Imports System.IO
Imports System.IO.Packaging
Imports System.Xml
Class GenerateDOCX
End Sub
End Class
Una vez que haya creado el nuevo paquete, el siguiente paso es crear uno o varios componentes y
serializar el contenido de los mismos. En el ejemplo del artículo de este mes, seguiremos las directrices
de una aplicación "Hello World", que requerirá la creación de un único componente denominado
\word\document.xml. Podemos crear un componente llamando al método CreatePart de un objeto
Package abierto y transfiriendo los parámetros para un URI y un tipo de contenido basado en cadenas:
Tenga en cuenta que todos los elementos de este documento XML se definen en el espacio de nombres
schemas.openxmlformats.org/wordprocessingml/2006/3/main según los formatos de archivo XML
abiertos de Office. El documento XML contiene un elemento de documento de alto nivel. En el elemento
del documento, hay un elemento de cuerpo que contiene el historial principal.
En el elemento de cuerpo, hay un elemento <p> para cada párrafo. En el elemento <p> hay una <r>
que define una ejecución. Una ejecución es una región de elementos que comparten el mismo conjunto
de características. En la ejecución, hay un elemento <t> que define un intervalo de texto, en este caso
de características. En la ejecución, hay un elemento <t> que define un intervalo de texto, en este caso
"Hello Word 2007".
Ahora vamos a generar el documento XML con código mediante la clase XmlDocument en el espacio de
nombres System.Xml. Si echa un vistazo a la figura 5, observará cómo el código crea estos elementos en
la estructura adecuada y con el espacio de nombres correspondiente. También debe tener en cuenta que
el código escribe el documento XML en la secuencia mediante el objeto StreamWriter y que, a
continuación, cierra la secuencia y descarga el contenido serializado en el objeto Package.
‘*** Define string variable for Open XML namespace for nsWP:
Dim nsWP As String = "http://schemas.openxmlformats.org" & _
"/wordprocessingml/2006/3/main"
‘*** Create the start part, set up the nested structure ...
Dim xmlPart As XmlDocument = New XmlDocument()
Dim tagDocument As XmlElement
tagDocument = xmlPart.CreateElement("w:document", nsWP)
xmlPart.AppendChild(tagDocument)
Dim tagBody As XmlElement
tagBody = xmlPart.CreateElement("w:body", nsWP)
tagDocument.AppendChild(tagBody)
Dim tagParagraph As XmlElement
tagParagraph = xmlPart.CreateElement("w:p", nsWP)
tagBody.AppendChild(tagParagraph)
Dim tagRun As XmlElement
tagRun = xmlPart.CreateElement("w:r", nsWP)
tagParagraph.AppendChild(tagRun)
Dim tagText As XmlElement
tagText = xmlPart.CreateElement("w:t", nsWP)
tagRun.AppendChild(tagText)
Ya hemos terminado de escribir el contenido XML en document.xml. El último paso consiste en crear una
relación entre el paquete y document.xml llamando al método CreateRelationship del objeto Package.
Este proceso resulta sencillo siempre que conozca el valor de cadena correcto para el tipo de relación y
pueda obtener un nombre único (como rId1) para la relación que va a crear:
También debe observar la llamada a Flush después de la llamada a CreateRelationship. Esta llamada
fuerza a la API de paquete a actualizar el elemento de relación de paquete con el elemento Relationship
adecuado. La última llamada al método Close del objeto Package termina la serialización del paquete y
libera el identificador de archivo de Hello.docx.
Ya hemos visto todos los pasos para generar un archivo .docx simple desde una aplicación de consola
escrita en Visual Basic® 2005, todo ello disponible aquí. Ahora vamos a escribir código para generar un
archivo .docx en el servidor desde una aplicación ASP.NET.
El código que acabamos de ver crea un objeto MemoryStream y, a continuación, serializa un archivo
.docx en él, simplemente de igual forma que se serializa un archivo .docx en un archivo físico. El código
del método WriteContentToPackage personalizado se tomó directamente del código interno de la
aplicación de consola anterior. No obstante, ahora crea un paquete y lo serializa en un búfer de memoria
en lugar de en un archivo físico.
Una vez escrito el paquete en el objeto MemoryStream y después de llamar a Close en el objeto de
paquete, termina el trabajo con la API de paquete. Todo lo que queda por hacer es configurar los
encabezados HTTP correspondientes y escribir el contenido del paquete en el cuerpo de la respuesta
que se va a devolver al cliente.
Comencemos con los encabezados HTTP. Se debe llamar a los métodos en el objeto de respuesta
ASP.NET para borrar cualquier encabezado existente y para agregar un encabezado contenido-
disposición que especifique un anexo con un nombre de archivo de Hello.docx:
Response.ClearHeaders()
Response.AddHeader("content-disposition", _
"attachment; filename=Hello.docx")
A continuación, debe establecer la codificación y el tipo de contenido MIME para la respuesta HTTP y
después escribir el contenido binario para el paquete en el cuerpo de la respuesta HTTP. Esto se puede
realizar mediante el código que aparece en la figura 7. Observe que las dos últimas llamadas a
Response.Flush y a Response.Close son necesarias para asegurarse de que el paquete completo está
escrito en su totalidad en la respuesta HTTP.
Figure 7 Response.ClearContent
Response.ClearContent()
Response.ContentEncoding = System.Text.Encoding.UTF8
Response.ContentType = "application/vnd.ms-word.document.12"
Siempre que el equipo cliente se ha configurado para comprender el tipo de contenido MIME asociado
a un archivo .docx, al usuario se le presentarán las opciones habituales: abra el documento en Word o
guárdelo en la unidad de disco local.
Si el usuario hace clic en el botón Abrir, el archivo .docx se abre automáticamente en Word como se
muestra en la figura 8. Es interesante observar que en este punto el paquete no se ha guardado nunca
como un archivo físico en el sistema de archivo. Se ha almacenado sólo en memoria, tanto en el servidor
web como en el equipo de escritorio cliente. Si el usuario cierra el documento sin guardarlo, es como si
nunca hubiera existido. Esto convierte a este enfoque en perfecto para la generación de plantillas de
cartas, notas, listas de clientes y cualquier tipo de documento en el que se pueda pensar.
figura 8 documento se abre en Word 2007 (Haga clic aquí para obtener una imagen más grande) (Hacer
clic en la imagen para ampliarla)