2009-04-13 48 views
6

Estoy trabajando en el proyecto Asp.Net que necesita completar un documento de Word. Mi cliente proporciona una plantilla de palabra con el apellido, el primer nombre, la fecha de nacimiento, etc. Tengo toda esa información en la base de datos sql, y el cliente quiere que los usuarios de la aplicación puedan descargar la palabra documento con información completa de la base de datos.¿Completa un documento de Word en asp.net?

¿Cuál es la mejor manera de archivar esto? Básicamente, necesito identificar esos "puntos que se pueden llenar" en un documento de Word, complete esa información cuando el usuario de la aplicación haga clic en el botón de descarga.

Respuesta

6

No estoy seguro acerca de algunos de los aspectos ASP.Net, pero estoy trabajando en algo similar y es posible que desee examinar el uso de un RTF. Puede usar el reemplazo de patrones en el RTF. Por ejemplo, puede agregar una etiqueta como {USER_FIRST_NAME} en el documento RTF. Cuando el usuario hace clic en el botón de descarga, su aplicación puede tomar la información de la base de datos y reemplazar cada instancia de {USER_FIRST_NAME} con los datos de la base de datos. Actualmente estoy haciendo esto con PHP y funciona muy bien. Word abrirá el RTF sin ningún problema, por lo que es otra razón por la que elegí este método.

2

He usado Aspose.Words for .NET. Es un poco costoso, pero funciona extremadamente bien y la API es bastante intuitiva para algo que es potencialmente muy complejo.

Si desea pre-diseñar sus documentos (o permitir que otros lo hagan por usted), cualquiera puede poner fields en el documento. Aspose puede abrir el documento, buscar y rellenar los campos, y guardar una nueva copia completa para descargar.

2

Aspose funciona bien, pero de nuevo: es caro.

Definitivamente evite la automatización de Office en aplicaciones web tanto como sea posible. Simplemente no escala bien.

Mi solución preferida para este tipo de problema es xml: específicamente aquí recomiendo WordProcessingML. Crea un documento Xml de acuerdo con el esquema, le pone una extensión .doc y MS Word lo abrirá como si fuera nativo en cualquier versión desde Office XP. Esto es compatible con la mayoría de las características de Word, y de esta manera puede reducir el problema de forma segura para reemplazar tokens en una secuencia de texto.

Tenga cuidado con Google para obtener más información al respecto: hay mucha confusión entre este y el nuevo formato basado en Xml para Office 2007. No son lo mismo.

8

Si puede usar Office 2007, el camino a seguir es utilizar la API de Open XML para formatear los documentos: http://support.microsoft.com/kb/257757. La razón por la que debe seguir esa ruta es que realmente no puede usar Word Automation en un entorno de servidor. (PUEDE, pero es un gran dolor para funcionar correctamente, y puede FÁCILMENTE romper).

Si no puede ir a la ruta 2007, en realidad he tenido bastante éxito al abrir una plantilla de palabra como una secuencia y buscar y reemplazar los tokens y servir eso al usuario. Esto realmente ha funcionado sorprendentemente bien en mi experiencia y es REALMENTE fácil de implementar.

+0

Las versiones anteriores de Word pueden abrir estos documentos; solo tienes que descargar una actualización de MS. – Will

+0

El estilo xml anterior en mi respuesta funcionará sin requerir ninguna actualización. –

-3

se puede no utilizar Microsoft de InterOp propio marco para utilizar la funcionalidad de Word

Ver Here

+1

Es una verdadera pena hacer que esto funcione y mantenerlo funcionando en un entorno de servidor. No recomendado. – Knobloch

1

Este código funciona para las cajas de texto y casillas de verificación WordML. Está basado en índices, así que simplemente pase una serie de cadenas para todos los cuadros de texto y una matriz de bool para todas las casillas de verificación.

public void FillInFields(
    Stream sourceStream, 
    Stream destinationStream, 
    bool[] pageCheckboxFields, 
    string[] pageTextFields 
    ) { 

    StreamUtil.Copy(sourceStream, destinationStream); 
    sourceStream.Close(); 

    destinationStream.Seek(0, SeekOrigin.Begin); 

    Package package = Package.Open(destinationStream, FileMode.Open, FileAccess.ReadWrite); 
    Uri uri = new Uri("/word/document.xml", UriKind.Relative); 

    PackagePart packagePart = package.GetPart(uri); 
    Stream documentPart = packagePart.GetStream(FileMode.Open, FileAccess.ReadWrite); 
    XmlReader xmlReader = XmlReader.Create(documentPart); 

    XDocument xdocument = XDocument.Load(xmlReader); 

    List<XElement> textBookmarksList = xdocument 
     .Descendants(w + "fldChar") 
     .Where(e => (e.AttributeOrDefault(w + "fldCharType") ?? "") == "separate") 
     .ToList(); 

    var textBookmarks = textBookmarksList.Select(e => new WordMlTextField(w, e, textBookmarksList.IndexOf(e))); 

    List<XElement> checkboxBookmarksList = xdocument 
     .Descendants(w + "checkBox") 
     .ToList(); 

    IEnumerable<WordMlCheckboxField> checkboxBookmarks = checkboxBookmarksList 
     .Select(e => new WordMlCheckboxField(w, e, checkboxBookmarksList.IndexOf(e))); 

    for (int i = 0; i < pageTextFields.Length; i++) { 
     string value = pageTextFields[i]; 
     if (!String.IsNullOrEmpty(value)) 
      SetWordMlElement(textBookmarks, i, value); 
    } 

    for (int i = 0; i < pageCheckboxFields.Length; i++) { 
     bool value = pageCheckboxFields[i]; 
     SetWordMlElement(checkboxBookmarks, i, value); 
    } 

    PackagePart newPart = packagePart; 
    StreamWriter streamWriter = new StreamWriter(newPart.GetStream(FileMode.Create, FileAccess.Write)); 
    XmlWriter xmlWriter = XmlWriter.Create(streamWriter); 
    if (xmlWriter == null) throw new Exception("Could not open an XmlWriter to 4311Blank-1.docx."); 
    xdocument.Save(xmlWriter); 

    xmlWriter.Close(); 
    streamWriter.Close(); 
    package.Flush(); 

    destinationStream.Seek(0, SeekOrigin.Begin); 
} 

private class WordMlTextField { 
    public int? Index { get; set; } 
    public XElement TextElement { get; set; } 

    public WordMlTextField(XNamespace ns, XObject element, int index) { 
     Index = index; 

     XElement parent = element.Parent; 
     if (parent == null) throw new NicException("fldChar must have a parent."); 
     if (parent.Name != ns + "r") { 
      log.Warn("Expected parent of fldChar to be a run for fldChar at position '" + Index + "'"); 
      return; 
     } 
     var nextSibling = parent.ElementsAfterSelf().First(); 

     if (nextSibling.Name != ns + "r") { 
      log.Warn("Expected a 'r' element after the parent of fldChar at position = " + Index); 
      return; 
     } 

     var text = nextSibling.Element(ns + "t"); 
     if (text == null) { 
      log.Warn("Expected a 't' element inside the 'r' element after the parent of fldChar at position = " + Index); 
     } 

     TextElement = text; 
    } 
} 

private class WordMlCheckboxField { 
    public int? Index { get; set; } 
    public XElement CheckedElement { get; set; } 
    public readonly XNamespace _ns; 

    public WordMlCheckboxField(XNamespace ns, XContainer checkBoxElement, int index) { 
     _ns = ns; 
     Index = index; 

     XElement checkedElement = checkBoxElement.Elements(ns + "checked").FirstOrDefault(); 
     if (checkedElement == null) { 
      checkedElement = new XElement(ns + "checked", new XAttribute(ns + "val", "0")); 
      checkBoxElement.Add(checkedElement); 
     } 

     CheckedElement = checkedElement; 
    } 

    public static void Copy(Stream readStream, Stream writeStream) { 
     const int Length = 256; 
     Byte[] buffer = new Byte[Length]; 
     int bytesRead = readStream.Read(buffer, 0, Length); 
     // write the required bytes 
     while (bytesRead > 0) { 
      writeStream.Write(buffer, 0, bytesRead); 
      bytesRead = readStream.Read(buffer, 0, Length); 
     } 
     readStream.Flush(); 
     writeStream.Flush(); 
    } 
+0

¿Tal vez podría agregar algo de contexto a la muestra? –

+0

¿Qué tipo de contexto tienes en mente? Básicamente terminé de implementar la funcionalidad exacta que gisresearch pedía (asumiendo WordML) y pensé que podría ser útil. Básicamente solo llame a FillInfields (...). –

0

En general se van a querer evitar hacer la automatización de oficinas en un Sever, y Microsoft tiene aún stated that it is a bad idea as well. Sin embargo, la técnica que generalmente uso es Office Open XML que se detectó en aquinas. Toma un poco de tiempo aprender a manejar el formato, pero vale la pena una vez que lo hace, ya que no tiene que preocuparse por algunos de los problemas relacionados con la automatización de Office (por ejemplo, procesos pendientes).

Hace un tiempo respondí una pregunta similar a esta que podría serle útil, puede encontrarla en here.

+0

hay un poco más de matiz que "MS dice que es malo". MS especifica que las API anteriores de 2003 no son recomendables, pero las nuevas cosas de Open XML son perfectas para esta tarea. – jfar

+0

@jfar - De hecho, hay más matices que eso, pero es una manera rápida de expandir las cosas. Además, notará que dije que estoy usando el formato Office Open XML. – rjzii

0

Si necesita hacer esto en archivos DOC (a diferencia de DOCX), entonces el SDK de OpenXML no lo ayudará.

Además, solo quiero agregar otro +1 sobre el peligro de automatizar las aplicaciones de Office en los servidores. Te encontrarás con problemas de escala, lo garantizo.

Para agregar otra referencia a una herramienta de terceros que se puede utilizar para resolver su problema:

http://www.officewriter.com

OfficeWriter le permite controlar Docs con una API completa, o un enfoque basado en plantillas (como cuál es su requerimiento) que básicamente le permite abrir, enlazar y guardar DOC y DOCX en escenarios como este con poco código.

Cuestiones relacionadas