lunes, 2 de enero de 2012

Leer un archivo Word con Java

Tal vez mas de una ocasión nos han pedido desarrollar una aplicación que tenga que entre otras cosas sea necesario leer un archivo de word, ya sea para un editor de textos o cualquier uso que se le encuentre a ello. Muchas veces vemos el ejemplo de como leer un archivo de texto .txt pero pocas veces practicamos como debemos hacerlo con un fichero de word .doc/.docx.

La verdad hay que ser honestos Java por si solo no es capaz de hacer la tarea (al menos no que yo sepa) por lo que tenemos dos opciones para hacer esto, la primera sería descomprimir el archivo .doc/.docx como si se tratase de un .zip, navegar los directorios y leer el .xml donde el texto se encuentra identificándolo por las etiquetas del documento, o bien el modo más fácil es usar una librería externa que probablemente haga la primera opción para conseguir el texto del word.

En este ejemplo veremos el modo fácil (no hay por que complicarse la vida), la librería que usaremos se llama POI y pertenece a la Fundación de Software de Apache, con dicha librería podremos manipular documentos XML y también de Microsoft Office en nuestra aplicación, dígase no solo word, sino también PowerPoint, Excel y no se que más. Sin más preámbulos descarguemos la Libreria POI y empecemos.


Recomiendo descargar la versión estable más reciente disponible yo trabajare con la 3.7 pero para efectos prácticos cualquier versión es igual, es igual. Vamos a ello, primero hay que tener en claro que es lo que vamos a abrir ya que el proceso difiere (solo un poco) de un .doc al de un .docx. Si nuestra aplicación debe abrir ambos deben implementar un método que reconozca la extensión del documento y así saber que es lo que se abrirá, pero eso ya es otra historia.

Los import necesarios son:
//Librerias de POI requeridas
import org.apache.poi.hwpf.extractor.WordExtractor;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;

//Librerias de JAVA requeridas
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

Lo primero es crear el objeto (File) que se va a referencia al archivo word:
//Guardar la ruta y recuerden que se debe poner doble barra \\
String ruta = "c:\\documento.docx"

//Se crea el objeto File con la ruta del archivo
File archivodoc = new File(ruta);

Ya que lo tenemos, necesitamos crear un Stream pasando el File creado que usara la POI para leerlo:
//Creamos el stream fijense bien los objetos usados
FileInputStream fis = new FileInputStream(archivodoc);
InputStream entradaArch = fis; 

Ahora usaremos la POI, crearemos el objeto con el que podremos manipular el documento, pero ojo este paso es distinto al de un .doc o un .docx, veamos los por separado:

Leer un .doc
Creamos el objeto WordExtractor que viene en la POI asi:
//Creamos el extractor pasandole el stream
WordExtractor we = new WordExtractor(entradaArch);

Y leemos el texto usando ese objeto creado:
//Leemos y guardamos en un String
String texto = we.getText();
	
//Lo imprimimos para probar
System.out.print(texto);


Leer un .docx
Cargamos el documento a un nuevo objeto que usara el extractor y creamos el extractor de contenido:
//Se crea un documento que la POI entiende pasandole el stream
XWPFDocument ardocx = new XWPFDocument(entradaArch); 
             
//instanciamos el obj para extraer contenido pasando el documento
XWPFWordExtractor xwpf_we = new XWPFWordExtractor(ardocx); 

Y leemos el texto usando ese objeto creado:
//leer el texto para un .docx 
String texto = xwpf_we.getText(); 

// se imprime 
System.out.println(texto); 

Eso es todo, cabe destacar que en la String donde obtenemos el texto se guarda todo el contenido, no es por linea como lo es al leer un .txt, aquí se lee todo en esa String, por cada salto de linea que tenga el documento la String lo tendrá, así que para separar por párrafos pueden usar el método .split("\n") asi dividirán por párrafos. Si el documento tiene imágenes analicen la documentación de la POI me parece que tiene objetos para eso.

Por ultimo, veamos le código completo en el cual me ahorrare pasos pero da el mismo resultado, Recuerden siempre que trabajemos con Streams (y varios objetos más) necesitamos usar un try y un catch para controlar las excepciones.
//Librerias de POI requeridas
import org.apache.poi.hwpf.extractor.WordExtractor; //Para leer un .doc
import org.apache.poi.xwpf.extractor.XWPFWordExtractor; //Para leer un XWPF Document
import org.apache.poi.xwpf.usermodel.XWPFDocument; //Para instanciar un .docx

//Librerias de JAVA requeridas
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

public class DocExtractor {
	public static void main(String [] args) {
		//Se crea el objeto File con la ruta del archivo
		//En la ruta recuerden que se debe poner doble barra "\\"
		File archivoDocx = new File("c:\\documento.docx");
		File archivoDoc = new File("c:\\documento.doc");
		
		try {
		//Creamos el stream fijense bien los objetos usados
		InputStream entradaArch1 = new FileInputStream(archivoDoc);
		InputStream entradaArch2 = new FileInputStream(archivoDocx);
		
		} catch(Exception ex) {
			//Manejar Excepcion IO y FileNotFound
		}
		
		//Metodos para leer dependiendo de si es .doc o .docx
		String textoDeDoc = leerDoc(entradaArch1);
		String textoDeDocx = leerDocx(entradaArch2);
		
		// se imprime
		System.out.println(textoDeDoc);
		System.out.println(textoDeDocx);

	} // End main
	
	private static String leerDoc(InputStream doc) {
		//Creamos el extractor pasandole el stream
		WordExtractor we = new WordExtractor(doc);
		
		//Regresamos lo leído		
		return we.getText();
	}
	
	private static String leerDocx(InputStream docx) {
		//Se crea un documento que la POI entiende pasandole el stream
		//instanciamos el obj para extraer contenido pasando el documento
		XWPFWordExtractor xwpf_we = new XWPFWordExtractor(new XWPFDocument(docx)); 
				
		return xwpf_we.getText();
	}
} //End class

Observen que aun se puede resumir aun más el código pero la verdad que se considera una mal hábito de programación, además se haría menos comprensible (eso creo), también me parece que al manipular los objetos de la POI puede haber excepciones, les dejo de tarea agregar el try y el catch para esos métodos. Un saludo!


38 comentarios:

  1. Respuestas
    1. Gracias por comentar, si hay dudas no dudes en preguntar. Saludos!

      Eliminar
  2. amigo super claro el tutorial, muy bueno pero tengo un problema con la librería ya que no me reconoce los import.. por que no existen en la librería que descarge desde apache
    import org.apache.poi.hwpf.extractor.WordExtractor;
    import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
    import org.apache.poi.xwpf.usermodel.XWPFDocument;

    hasta org.apache.poi todo bien pero hwpf,xwpf no existen en la librería, si puedes subir la que usas tu espectacular, yo me baje la oficial de apache (3.8) y además me descargue una (3.7) y no contienen esas librerías

    espero tu ayuda de ante mano gracias :)

    ResponderEliminar
    Respuestas
    1. Recuerda que necesitas agregar la librería a tu proyecto, hice una entrada sobre como agregar librerías si es que no sabes como, en si la que es para .doc viene en "poi-scracthpad" y la que es para .docx viene en "poi-ooxml" checa, si vienen =)

      Eliminar
    2. estimado, si agregue la librería, el problema es que no vienen las que necesito por eso quería que me mandaras la tuya

      Eliminar
    3. La 3.7 que use en el tutorial ya no la tengo, ya que esta entrada ya tiene un timpo de haberla hecho. Acabo de descargar "poi-bin-3.8-20120326.zip ( 23MB, signed) " lo descomprimes, y vienen varios archivos .jar que son los que contienen las clases a usar, el que tiene la clase para los .doc se llama "poi-scratchpad-3.8-20120326.jar" y el que trae la clase para los docx se llama "poi-ooxml-3.8-20120326.jar" como vez es lo mismo que te había dicho, no se cual sea tu detalle, pero las clases están alli, acabo de descargar el POI, o comenta, como le estas haciendo que es lo que importas, o que es lo que agregas a tu proyecto?

      Eliminar
    4. estimado me acabo de dar cuenta del error, había bajado el poi-src por lo cual tenia qe compilarlo para conseguir los .jar ahora funciono lo de abrir el .doc pero el .docx me envia error y estoy tratando de solucionarlo

      gracias por la ayuda y por los tutoriales estan muy buenos :D

      Eliminar
    5. De nada, el paquete src mas que nada es para los developers que van a mejorar el proyecto, no se que error te pueda dar, solo recuerda que difiere el proceso de un .doc a un .docx, un poco pero difiere en los objetos a usar, saludos!

      Eliminar
    6. haber es cierto lo que dicen solo se puede habrir el .doc e intentado miles de formas pero el neat beans me da estos errores

      run:
      Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/xmlbeans/XmlException
      at docextractor.DocExtractor.main(DocExtractor.java:29)
      Caused by: java.lang.ClassNotFoundException: org.apache.xmlbeans.XmlException
      at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
      at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
      at java.security.AccessController.doPrivileged(Native Method)
      at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
      at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
      at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
      at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
      ... 1 more

      ha ver si me pueden ayudar por favor

      Eliminar
    7. No bajaste el paquete correcto o no importaste las librerías. Revisa tu librería y busca y comprueba que este la clase org.apache.xmlbeans.XmlException si esta verifica que este compilada y no en source

      Eliminar
    8. Es total mente eso, necesitas importar la clase org.apache.xmlbeans.XmlException esta dentro de la carpeta ooxml-lib donde tienes guardada la carpeta de la libreria en tu pc local.

      Luego desde netbeans le das al menu de arriba Tools--> Libraries --> Buscas la libreria Poi que has añadido --> le añades el fichero .jar llamado xmlbeans-2.6.0.jar y funciona perfectamente con los .docx !

      Eliminar
  3. Muchas gracias me sirvio mucho el tutorial, aunque tengo una duda, ¿Hay alguna forma de extraer el texto con sus propiedades? es decir tamaño, tipo de fuente,etc.

    ResponderEliminar
    Respuestas
    1. La verdad no sabría decirte nunca hice eso en la practica, pero podrías checar la documentación en la página de Apache para ver si hay algo al respecto, en teoría es posible ya que todo eso son propiedades seteadas con un xml. Gracias por leer la entrada =)

      Eliminar
  4. Saludos hermano me sale el siguiente error cuando intento abrir un .docx. el .doc si lo abre sin problema tienes alguna recomendacion?

    The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)

    ResponderEliminar
    Respuestas
    1. El error que pusiste dice que estas intentando abrir un .docx como un .doc y eso no se puede, checa el manual, hago mucho énfasis en que esos archivos se abren de manera distinta. El proceso es muy parecido pero no es igual checa bien.

      Eliminar
  5. Una pregunta no tendrás un ejemplo de como guardar un *.docx en formato PDF te lo agradecería.

    ResponderEliminar
  6. No amigo, la verdad nunca hice una practica así, pero es buena idea, haber si se me hace un tiempito para publicar algo al respecto, gracias por comentar!

    ResponderEliminar
  7. Hola... en mi caso necesito que se ingrese una información que tengo en la BD a un documento dada una estructura... por casualidad sabes como podría hacerlo?

    ResponderEliminar
    Respuestas
    1. Tal vez te sirva la librería JasperReports y iReports, busca más sobre el tema, se que te sirve para obtener informacion de una base de datos a documento, checa en google...

      Eliminar
  8. Hola, estoy utilizando la libreria 3.9 y me da varios errores con las excepciones. He utilizado try catch y siempre me sale impreso lo que hay en el catch, nunca el texto. ¿Sabrías por que?

    Si necesitas mi codigo dimelo.

    Un saludo gracias.

    ResponderEliminar
    Respuestas
    1. Deberias leer el error/excepcion que te da, y en base al mensaje buscar la solución.. recuerda para solucionar el problema es necesario identificarlo primero, saludos!

      Eliminar
  9. Estoy creando un proyecto en web para el cual del lado del servidor programo en lenguaje java y quiero utilizar esta librería para obtener la información de un documento word pero no me pincha y la probé en una simple aplicación de escritorio y veo que si funciona por lo que no se cual podría ser el problema sabes si es que para las aplicaciones web no funciona para no romperme mas la cabeza. Para mas información estoy programando en eclipse con el server jboss-4.2.2, con el framework seam.

    ResponderEliminar
    Respuestas
    1. No te sabría decir amigo, nunca la probé en una web application, en teoría debe de ser la misma, si el archivo te llega correctamente debería funcionar pero a ciencia cierta no lo sé

      Eliminar
  10. Buenas:
    Lo primero de todo muchas gracias por la explicación que has hecho sobre esta librería. Tenía una duda que no se si sabrás la respuesta. Estoy haciendo un programa que sirve para poder leer documentos que están en formato .docx . El caso, es que en el ejemplo pones como extraer el texto de un documento, pero es un texto "llano". Para mi programa, me gustaría poder extraer (en un string) un XML entero ó un HTML para poder incrustarlo en un JTextPane...Lo que necesito realmente es que en el JTextPane aparezca el texto TAL Y COMO LO VERÍAS EN ABRIÉNDOLO CON EL WORD, es decir, que pueda haber palabras que estén en negrita, que pueda haber tablas, imágenes etc. No puedo sacar únicamente el contenido sin formato, me gustaría poder coger el formato también...¿Sabrás como se hace? he mirado en Internet y no he conseguido encontrar un ejemplo de lo que te he dicho anteriormente.
    Un saludo y gracias de antemano

    ResponderEliminar
    Respuestas
    1. Bueno, si a un .docx le cambias la extensión a .zip verás que tiene varios directorios de carpeta, y en uno de los archivos esta el contenido en XML, con las etiquetas que pone el word, entonces puedes hacer que tu programa tome el archivo le cambie la extension y lo extraiga.. por otro lado no estoy seguro pero se me hace que el JTextPane no acepta texto formateado y si lo hace dudo que sea con la misma sintaxis XML del MS Word.. puedes revisar el proyecto de OpenOffice o LibreOffice me parece que estan en Java y al fin y al cabo ellos abren esos formatos, podrias ver en su codigo fuente como es que lo hacen... Es lo que te puedo decir, lamento no poder ser de más ayuda, suerte!

      Eliminar
  11. Buen dia,
    Intento trabajar con esta librería,
    para casos practicos en mi empresa me piden eliminar del archivo xlsx unas filas siempre y cuando cumplan con un condicional. Leí por ahi queeso se hace con RemoveRow, pero realmente no me está corriendo bien. Me saca error. Por cosas de la vida , sabes como implementar este caso puntual. Necesito eliminar, mas no dejar en blanco la fila determinada sipor ejemplo dicha fila tiene la palabra "Credito".

    Ando haciendo esto:


    public static void getManualSkuRepository() throws IOException, SymphonyException {

    //Se obtiene el repositorio desde archivo Excel.
    Sheet sheet = getDeleteSkuParametersSheet(Utils.symphonyProperties.getString("FILES_PATH"),
    Utils.symphonyProperties.getString("DELETEPARAMETERS_FILE_NAME"));
    DeleteDTO sku = null;
    //se comienza a recorrer.
    for (Row row : sheet) {
    // se valida que no se toquen las dos primeras filas, son cabeceras y nombres
    /* if (SymphonyUtils.validateRow(row)) {
    sku = new DeleteDTO();
    //se toman los datos que se quieren recuperar en el archivo plano
    getDataFromRow(row, sku);

    if ( sku.getWarehouseName().contains("ACCESORIOS")||(sku.getWarehouseName().contains("REPUESTOS")))
    {

    }*/
    sheet.removeRow(row);

    }

    ResponderEliminar
    Respuestas
    1. No amigo, una disculpa no se como se haga lo que quieres hacer, pero revisa el error que te dé, busca ese error en google es muy seguro que alguien más lo halla tenido antes.

      Eliminar
  12. y como es para insertar una imagen a un word utilizando POI?

    ResponderEliminar
  13. Buen tutorial, pero tengo una pregunta, esto es para leer el documento, pero como puedo modificar el documento, es decir como puedo agregarle una linea?

    ResponderEliminar
  14. hola que tal o vengo probando apenas entre en esto y mira me sale este error ????????
    Error c:\users\electronicos\Mis documentos\APIeditor.doc (El sistema no puede encontrar el archivo especificado)
    Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/poi/POIOLE2TextExtractor
    at java.lang.ClassLoader.defineClass1(Native Method)

    ResponderEliminar
    Respuestas
    1. Tal cual como dice el mensaje, el archivo no se encuentra, por obvias razones es que la ruta esta mal, por ejemplo ese espacio que tiene no creo que valla. saludos

      Eliminar
  15. Disculpa, se necesita otro jar aparte de apache poi 2.7?

    ResponderEliminar
  16. esta libreria soporta .doc o .docs con imagenes

    ResponderEliminar
  17. Hola amigo este quiero visualizar el contenido pero no en consola sino en un textarea u otro se podra...???

    ResponderEliminar
  18. Amigo.. excelente post. como puedo usar un archivo que tengo en una ruta especifica y escribir en el...gracias!

    ResponderEliminar

Deja un comentario! =)