Android中使用POI加载与显示word文档

来源:互联网 发布:java javascript区别 编辑:程序博客网 时间:2024/05/16 05:03

最近打算实现一个功能:在Android中加载显示Word文档,当然这里不是使用外部程序打开。查看一些资料后,打算采用poi实现,确定了以下实现思路:

  1. 将ftp中的word文档下载到本地。
  2. 调用poi将word文档转成html格式并保存到本地
  3. 使用WebViewer加载显示本地html

这里略去下载word文档到本地不谈,仅仅后面两步,看起来还是比较简单的,网上也有相关代码。不过在使用过程中遇到了两个大的问题,着实让笔者费了一番脑筋。这里给大家列出来,希望能帮助大家节省些时间。

 
首先,说一下POI使用方法
  1. 下载poi-bin-3.9-20121203.tar.gz并解压,提取查看Office文档所依赖的包。
  2. word相关操作依赖于poi-3.9-20121203.jar和poi-scratchpad-3.9-20121203.jar两个包,将其加入到Android程序的libs文件夹中。
  3. 将word转html并保存到本地,然后使用WebViewer加载显示本地html。整个代码如下


[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.example.office;  
  2.   
  3. import java.io.BufferedWriter;  
  4. import java.io.ByteArrayOutputStream;  
  5. import java.io.File;  
  6. import java.io.FileInputStream;  
  7. import java.io.FileNotFoundException;  
  8. import java.io.FileOutputStream;  
  9. import java.io.IOException;  
  10. import java.io.OutputStreamWriter;  
  11. import java.util.List;  
  12.   
  13. import javax.xml.parsers.DocumentBuilderFactory;  
  14. import javax.xml.parsers.ParserConfigurationException;  
  15. import javax.xml.transform.OutputKeys;  
  16. import javax.xml.transform.Transformer;  
  17. import javax.xml.transform.TransformerException;  
  18. import javax.xml.transform.TransformerFactory;  
  19. import javax.xml.transform.dom.DOMSource;  
  20. import javax.xml.transform.stream.StreamResult;  
  21.   
  22.   
  23. import org.apache.poi.hwpf.HWPFDocument;  
  24. import org.apache.poi.hwpf.converter.PicturesManager;  
  25. import org.apache.poi.hwpf.converter.WordToHtmlConverter;  
  26. import org.apache.poi.hwpf.usermodel.Picture;  
  27. import org.apache.poi.hwpf.usermodel.PictureType;  
  28. import org.w3c.dom.Document;  
  29.   
  30. import android.os.Bundle;  
  31. import android.app.Activity;  
  32. import android.webkit.WebSettings;  
  33. import android.webkit.WebView;  
  34.   
  35. public class MainActivity extends Activity {  
  36.       
  37.     private String docPath = "/mnt/sdcard/documents/";  
  38.     private String docName = "test.doc";  
  39.     private String savePath = "/mnt/sdcard/documents/";      
  40.           
  41.     @Override  
  42.     public void onCreate(Bundle savedInstanceState) {  
  43.         super.onCreate(savedInstanceState);  
  44.         setContentView(R.layout.activity_main);  
  45.         String name = docName.substring(0, docName.indexOf("."));  
  46.         try {  
  47.             if(!(new File(savePath+name).exists()))  
  48.                 new File(savePath+name).mkdirs();  
  49.             convert2Html(docPath+docName,savePath+name+".html");  
  50.         } catch (Exception e){  
  51.             e.printStackTrace();  
  52.         }  
  53.         //WebView加载显示本地html文件  
  54.         WebView webView = (WebView)this.findViewById(R.id.office);         
  55.         WebSettings webSettings = webView.getSettings();  
  56.         webSettings.setLoadWithOverviewMode(true);      
  57.         webSettings.setSupportZoom(true);  
  58.         webSettings.setBuiltInZoomControls(true);  
  59.         webView.loadUrl("file:/"+savePath+name+".html");  
  60.     }  
  61.       
  62.     /** 
  63.      * word文档转成html格式  
  64.      * */  
  65.     public void convert2Html(String fileName, String outPutFile)    
  66.             throws TransformerException, IOException,    
  67.             ParserConfigurationException {    
  68.         HWPFDocument wordDocument = new HWPFDocument(new FileInputStream(fileName));  
  69.         WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(  
  70.                 DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument());         
  71.           
  72.         //设置图片路径  
  73.         wordToHtmlConverter.setPicturesManager(new PicturesManager()    
  74.          {    
  75.              public String savePicture( byte[] content,    
  76.                      PictureType pictureType, String suggestedName,    
  77.                      float widthInches, float heightInches )    
  78.              {    
  79.                  String name = docName.substring(0,docName.indexOf("."));  
  80.                  return name+"/"+suggestedName;    
  81.              }    
  82.          } );  
  83.       
  84.         //保存图片  
  85.        List<Picture> pics=wordDocument.getPicturesTable().getAllPictures();    
  86.         if(pics!=null){    
  87.             for(int i=0;i<pics.size();i++){    
  88.                 Picture pic = (Picture)pics.get(i);    
  89.                 System.out.println( pic.suggestFullFileName());   
  90.                 try {    
  91.                     String name = docName.substring(0,docName.indexOf("."));  
  92.                     pic.writeImageContent(new FileOutputStream(savePath+ name + "/"  
  93.                             + pic.suggestFullFileName()));  
  94.                 } catch (FileNotFoundException e) {    
  95.                     e.printStackTrace();    
  96.                 }      
  97.             }    
  98.         }  
  99.         wordToHtmlConverter.processDocument(wordDocument);  
  100.         Document htmlDocument = wordToHtmlConverter.getDocument();    
  101.         ByteArrayOutputStream out = new ByteArrayOutputStream();  
  102.         DOMSource domSource = new DOMSource(htmlDocument);  
  103.         StreamResult streamResult = new StreamResult(out);  
  104.     
  105.         TransformerFactory tf = TransformerFactory.newInstance();    
  106.         Transformer serializer = tf.newTransformer();    
  107.         serializer.setOutputProperty(OutputKeys.ENCODING, "utf-8");    
  108.         serializer.setOutputProperty(OutputKeys.INDENT, "yes");    
  109.         serializer.setOutputProperty(OutputKeys.METHOD, "html");  
  110.         serializer.transform(domSource, streamResult);    
  111.         out.close();    
  112.         //保存html文件  
  113.         writeFile(new String(out.toByteArray()), outPutFile);   
  114.     }  
  115.       
  116.     /** 
  117.      * 将html文件保存到sd卡 
  118.      * */  
  119.     public void writeFile(String content, String path) {    
  120.         FileOutputStream fos = null;    
  121.         BufferedWriter bw = null;    
  122.         try {    
  123.             File file = new File(path);    
  124.             if(!file.exists()){  
  125.                 file.createNewFile();  
  126.             }                  
  127.             fos = new FileOutputStream(file);    
  128.             bw = new BufferedWriter(new OutputStreamWriter(fos,"utf-8"));    
  129.             bw.write(content);    
  130.         } catch (FileNotFoundException fnfe) {    
  131.             fnfe.printStackTrace();    
  132.         } catch (IOException ioe) {    
  133.             ioe.printStackTrace();    
  134.         } finally {    
  135.             try {    
  136.                 if (bw != null)    
  137.                     bw.close();    
  138.                 if (fos != null)    
  139.                     fos.close();    
  140.             } catch (IOException ie) {    
  141.             }    
  142.         }    
  143.     }  
  144. }  

activity_main.xml如下
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent" >  
  5.   
  6.     <WebView  
  7.         android:id = "@+id/office"  
  8.         android:layout_width="wrap_content"  
  9.         android:layout_height="wrap_content"  
  10.         android:layout_centerHorizontal="true"  
  11.         android:layout_centerVertical="true"  
  12.         android:text="@string/hello_world"  
  13.         tools:context=".MainActivity"/>  
  14. </RelativeLayout>  

上面代码中convert2Html用于将word文档转换html。下面的代码则是使用WebViewer加载显示本地html文件。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. WebView webView = (WebView)this.findViewById(R.id.office);         
  2.        WebSettings webSettings = webView.getSettings();  
  3.        webSettings.setLoadWithOverviewMode(true);      
  4.        webSettings.setSupportZoom(true);  
  5.        webSettings.setBuiltInZoomControls(true);  
  6.        webView.loadUrl("file:/"+savePath+name+".html");  

下面来详细说说存在的两个问题 

问题一:使用时有如下报错:

09-23 17:40:12.350: W/System.err(29954): java.lang.NullPointerException
09-23 17:40:12.350: W/System.err(29954):      at org.apache.poi.hwpf.converter.AbstractWordUtils.compactChildNodesR(AbstractWordUtils.java:146)

 
这个是POI自身的bug,具体原因在于AbstractWordUtils.java中没有对child2.getParent是否为空进行判断。将如下代码
child2.getParentNode().removeChild( child2 );i--;

更改为

if(child2.getParentNode()!=null){  child2.getParentNode().removeChild( child2 );  i--;}

然而这里需要重新编译AbstractWordUtils.java类,将源工程下载后,找到AbstractWordUtils.java后,试验了以下方法。

  1. 直接使用javac编译,会提示很多类库找不到
  2. 使用反编译工具,反编译后更改个文字还可以,更改代码就有点勉强了。
  3. 将整个poi导入eclipse后重新编译,工作量太大,没有进行尝试。
最后绞尽脑汁还是想到了一个相当简单的方法(高手请飘过~),为此还得瑟了几分钟。具体如下:
  1. 将AbstractWordUtils.java,poi-3.9-20121203.jar,poi-scratchpad-3.9-20121203.jar放到同一目录下,非必需
  2. 通过引用已有的两个包进行编译,编译命令如下:javac -cp d:\poi-3.9-20121203.jar;d:\poi-scratchpad-3.9-20121203.jar; d:\AbstractWordUtils.java ;编译后生成AbstractWordUtils.class文件。
  3. 将poi-3.9-20121203.jar的后缀改成zip,将AbstractWordUtils.class拖到zip中覆盖掉原有文件,然后将后缀zip改成jar即可。点击此处下载更改好的poi-3.9-20121203.jar。
问题二:找不到HWPFDocument错误:java.lang.NoClassDefFoundError: org.apache.poi.hwpf.HWPFDocument或者内存不足问题:Unable to execute dex: Java heap space
 
上述问题取决于使用poi-3.9-20121203.jar,poi-scratchpad-3.9-20121203.jar包的不同方式。
 
如果将两个jar包放在libs目录下,就不会出现类找不到的错误;但很可能会出现内存不足的问题。笔者开始通过更改eclipse安装文件夹下的eclipse.ini文件增大内存到512M,解决了内存不足的问题;后来加入到另外一个更大的程序后,又出现内存不足的问题,调整到800M解决。值得注意的是,如果把最大值调整到1024M,eclipse就无法启动了(和你的机器相关),这实在不能算是个好的解决方案。以下为笔者机器上修改后eclipse.ini文件,注意标红的部分。
复制代码
 -startupplugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar--launcher.libraryplugins/org.eclipse.equinox.launcher.win32.win32.x86_1.1.100.v20110502-showsplashorg.eclipse.platform--launcher.XXMaxPermSize256m--launcher.defaultActionopenFile-vmargs-Xms256m-Xmx800m
复制代码
 
如果通过使用Add Library的方法加载jar包,就不会出现内存的问题,但是会出现类找不到的的问题:java.lang.NoClassDefFoundError: org.apache.poi.hwpf.HWPFDocument。虽然csdn上有人通过将新增的user lib放置到最上面的方法解决了,但我试了下没有生效,不得已还是采用了第一种方法。这里也希望解决了该问题的人能够留下评论或联系方式,方便请教。
 
最后,补充几点
  1. 目前poi只针对2003的doc格式,不支持2007及其以上的docx格式。
  2. 经测试发现,偶尔会出现的问题,不知如何解决。这里建议内部程序简单预览,外部程序打开word文档详细浏览的方式。
  3. poi和android API的版本或ADT版本有关;有的在java环境下良好,在android环境下就有问题,还请多多注意。
  4. 整个工程实例代码请点击此处。

转载来自:http://www.cnblogs.com/esrichina/p/3347454.html


0 0