当前位置: 首页 > 工具软件 > Apache HDT > 使用案例 >

apache poi操作word(03,07)踩坑

葛泳
2023-12-01

项目中业务需要生成用户签名后的协议文档,之前协议是在页面中展示并生成签名,简单用js插件canvas绘制生成pdf文件存储,但是word文档业务中不允许展示word协议,只能通过java服务端去手动生成。

例行 google,baidu去搜前辈们的代码,发现了几种方法。

1:java2word提供了一些简单 的api,可以生成,插入(图片/文本)具体没有去实际开发不做多说,但是需要office的一个组件供调用,所以生产环境是linux的就gg了。

2:itext 支持word操作相对比较友好,但是格式 设置可能比较 麻烦,本来这些都是比较抽象的。但是itext生成的文件都是rtf格式的,可能有些同学保存流 0/的时候直接后缀就是.doc或者.docx。再打开可能会出问题。

3:apache的poi自家产品,操作excel是6的飞起,操作word真心难受但是时间原因还是选择了poi(因为有前辈们的代码)

直接贴代码

只能替换文本,其他的功能并没有去深究,需要 预先做好模板,需要替换的文本内容用占位符替换。代码中有一段读取图片文件的但是没有成功。而且生成新word的时候,文件中的图片会丢失。

/**
	 * poi操作word2003版本,只能替换文本,不能修改图片和生成新图片
	 * 
	 * @throws Exception
	 */
	public static void readWord() throws Exception {

		InputStream istream = new FileInputStream(new File(path));
		HWPFDocument hdt = new HWPFDocument(istream);
		Range range = hdt.getRange();
		range.replaceText("${name}", "FY20171001982");// 使用资源占位符便捷替换模板中的数值
		range.replaceText("${no}", "11111");//替换第二个.....以此等等
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		FileOutputStream f = new FileOutputStream("E:/logs/xin.doc");
		int numchar = hdt.characterLength();
		PicturesTable s = hdt.getPicturesTable();
		for (int i = 0; i < numchar; i++) {
			Range ran = new Range(i, i + 1, hdt);
			CharacterRun cr = ran.getCharacterRun(0);
			boolean has = s.hasPicture(cr);
			if (has) {
				System.out.println("boolean==true");
				Picture pic = s.extractPicture(cr, false);
				pic.writeImageContent(new FileOutputStream(new File(
						"E:/logs/bmp.png")));
			}

		}
		hdt.write(os);
		f.write(os.toByteArray());
		f.close();
		os.close();
	}

操作2007版本的word(后缀.docx)

如果模板是03 的 就费几十秒用wps或者office重新保存成07版本的,以下代码能成功替换文本和图片。并且能插入图片,但是需要预先在模板中用占位符预订替换位置,这里只说对模板的替换和修改。生成预定格式的word建议还是不用poi,简单试了下很难受。(下边贴代码)

import java.io.IOException;
import java.io.InputStream;

import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;

public class CustomXWPFDocument extends XWPFDocument{
	 public CustomXWPFDocument(InputStream in) throws IOException {  
	        super(in);  
	    }  
	      
	    public CustomXWPFDocument() {  
	        super();  
	    }  
	      
	    public CustomXWPFDocument(OPCPackage pkg) throws IOException {  
	        super(pkg);  
	    }  
	      
	    public void createPicture(int id, int width, int height,XWPFParagraph paragraph) {  
	        final int EMU = 9525;  
	        width *= EMU;  
	        height *= EMU;  
	        String blipId = getAllPictures().get(id).getPackageRelationship()  
	                .getId();      
	      
	        CTInline inline = paragraph.createRun().getCTR()      
	                .addNewDrawing().addNewInline();      
	      
	        String picXml = ""      
	                + "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">"      
	                + "   <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"      
	                + "      <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"      
	                + "         <pic:nvPicPr>" + "            <pic:cNvPr id=\""      
	                + id      
	                + "\" name=\"Generated\"/>"      
	                + "            <pic:cNvPicPr/>"      
	                + "         </pic:nvPicPr>"      
	                + "         <pic:blipFill>"      
	                + "            <a:blip r:embed=\""      
	                + blipId      
	                + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>"      
	                + "            <a:stretch>"      
	                + "               <a:fillRect/>"      
	                + "            </a:stretch>"      
	                + "         </pic:blipFill>"      
	                + "         <pic:spPr>"      
	                + "            <a:xfrm>"      
	                + "               <a:off x=\"0\" y=\"0\"/>"      
	                + "               <a:ext cx=\""      
	                + width      
	                + "\" cy=\""      
	                + height      
	                + "\"/>"      
	                + "            </a:xfrm>"      
	                + "            <a:prstGeom prst=\"rect\">"      
	                + "               <a:avLst/>"      
	                + "            </a:prstGeom>"      
	                + "         </pic:spPr>"      
	                + "      </pic:pic>"      
	                + "   </a:graphicData>" + "</a:graphic>";      
	      
	        inline.addNewGraphic().addNewGraphicData();  
	        XmlToken xmlToken = null;  
	        try {      
	            xmlToken = XmlToken.Factory.parse(picXml);  
	        } catch (XmlException xe) {      
	            xe.printStackTrace();      
	        }      
	        inline.set(xmlToken);      
	      
	        inline.setDistT(0);      
	        inline.setDistB(0);      
	        inline.setDistL(0);      
	        inline.setDistR(0);      
	      
	        CTPositiveSize2D extent = inline.addNewExtent();      
	        extent.setCx(width);      
	        extent.setCy(height);      
	      
	        CTNonVisualDrawingProps docPr = inline.addNewDocPr();      
	        docPr.setId(id);      
	        docPr.setName("图片" + id);      
	        docPr.setDescr("descr");      
	    }      
}//该类是创建图片在word文本的中的节点。word转为xml之后可以看到对应的节点,这个类不用修改,直接使用就行

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.poi.POIXMLDocument;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;

public class POIReadAndWriteWord2007 {
	public static void main(String[] args) {  
        /**源文件的路径,注:只支持word2007,或许还支持word 2010,其他待测试*/  
        String filePath = "E:\\logs\\pro07.docx";  
        String tips = POIReadAndWriteWord2007.readwriteWord(filePath,new HashMap<String,String>());  
        System.out.println(tips);  
    }  
    /**读取并操作word2007中的内容*/  
    public static String readwriteWord(String filePath,Map<String,String> map){  
        File isExist = new File(filePath);  
        /**判断源文件是否存在*/  
        if(!isExist.exists()){  
            return "源文件不存在!";  
        }  
        CustomXWPFDocument document;  
        try {  
            /**打开word2007的文件*/  
            OPCPackage opc = POIXMLDocument.openPackage(filePath);  
            document = new CustomXWPFDocument(opc);  
            /**替换word2007的纯文本内容(段落内容表格中的不会读取到)*/  
            List<XWPFRun> listRun;  
            List<XWPFParagraph> listParagraphs = document.getParagraphs();  
            for (int i = 0; i < listParagraphs.size(); i++) {  
                listRun = listParagraphs.get(i).getRuns();  
                for (int j = 0; j < listRun.size(); j++) {  
//                	String text=listRun.get(j).getText(0);
//                	text.replace("${name}", "11111");
//                	listRun.get(j).setText(text);
                }  
            }
            /**替换表格中的文字**/
            Iterator<XWPFTable> itTable = document.getTablesIterator();  
            XWPFTable tables;  
            int rowsCount;  
            while (itTable.hasNext()) {  
                tables = itTable.next();  
                rowsCount = tables.getNumberOfRows();  
                for (int i = 0; i < rowsCount; i++) {  
                    XWPFTableRow row = tables.getRow(i);  
                    List<XWPFTableCell> cells = row.getTableCells();  
                    for (XWPFTableCell cell : cells) {  
                        for (Entry<String, String> e : map.entrySet()) {  
                            if (cell.getText().equals(e.getKey())) {  
                                cell.removeParagraph(0);  
                                cell.setText(e.getValue());  
                            }  
                        }  
                    }  
                }  
            }  
            
            /**取得文本的所有表格*/  
            Iterator<XWPFTable> it = document.getTablesIterator();  
            while(it.hasNext()){/**循环操作表格*/  
                XWPFTable table = it.next();  
                List<XWPFTableRow> rows = table.getRows();  
                for(XWPFTableRow row:rows){/**取得表格的行*/  
                    List<XWPFTableCell> cells = row.getTableCells();  
                    for(XWPFTableCell cell:cells){/**取得单元格*/  
                    	System.out.println(cell.getText());
                        if("#{img}#".equals(cell.getText())){/**判断单元格的内容是否为需要替换的图片内容*/
                        	System.out.println("替换图片?》》》》》》》》》》》》》》》》》》》》》");
                            File pic = new File("E:/logs/test22.png");  
                            FileInputStream is = new FileInputStream(pic);  
                            cell.removeParagraph(0);  
                            XWPFParagraph pargraph = cell.addParagraph();  
                            document.addPictureData(is, XWPFDocument.PICTURE_TYPE_PNG);  
                            document.createPicture(document.getAllPictures().size()-1, 260, 100, pargraph);  
                            if(is != null){  
                                is.close();  
                            }  
                        }  
                        List<XWPFParagraph> pars = cell.getParagraphs();  
                        for(XWPFParagraph par:pars){  
                            List<XWPFRun> es = par.getRuns();  
                            for(XWPFRun run:es){  
                                run.removeBreak();  
                            }  
                        }  
                        cellParagraph(cell); 
                    }  
                }  
            }  
            String downloadPath = "E:/logs/replace.docx";  
            OutputStream os = new FileOutputStream(downloadPath);  
            document.write(os); 
            //document.close();
            if(os != null){  
                os.close();  
            }  
            if(opc != null){  
                opc.close();  
            }  
            return "文件转换成功!路径为:"+downloadPath;  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return filePath;  
    }
   /**
    * 处理表格中的段落进行文本替换(表格中的文本用占位符会被单独隔开) 
    * @param cell
    */
   public static void cellParagraph(XWPFTableCell cell){
	   Map<String,String> map=new HashMap<String, String>();
       map.put("name", "111111111");
	   Iterator<XWPFParagraph> itPara = cell.getParagraphs().iterator();//.getParagraphsIterator();  
       String text;  
       Set<String> set;  
       XWPFParagraph paragraph;  
       List<XWPFRun> runs;  
       String key;  
       while (itPara.hasNext()) {  
           paragraph = itPara.next();  
           set = map.keySet();  
           Iterator<String> iterator = set.iterator();  
           while (iterator.hasNext()) {  
               key = iterator.next();  
               runs = paragraph.getRuns();  
               for (int i = 0, runSie = runs.size(); i < runSie; i++) {  
                   text = runs.get(i).getText(runs.get(i).getTextPosition());
                   System.out.println(text);
                   if (text != null && text.equals(key)) {  
                       runs.get(i).setText(map.get(key), 0);  
                   }  
               }  
           }  
       }
   }
    
    /**复制文件的方法poi操作2007会将源文件一并修改,不知道为毛,所以将模板复制一份*/  
    public static void copyFile(String oldPath, String newPath) {  
        try {  
            int bytesum = 0;  
            int byteread = 0;  
            File oldfile = new File(oldPath);  
            if (oldfile.exists()) { //文件存在时  
                InputStream inStream = new FileInputStream(oldPath); //读入原文件  
                FileOutputStream fs = new FileOutputStream(newPath);  
                byte[] buffer = new byte[1444];  
                while ( (byteread = inStream.read(buffer)) != -1) {  
                    bytesum += byteread; //字节数 文件大小  
                    System.out.println(bytesum);  
                    fs.write(buffer, 0, byteread);  
                }  
                inStream.close();  
                fs.close();  
            }  
        }  
        catch (Exception e) {  
            System.out.println("复制单个文件操作出错");  
            e.printStackTrace();  
        }  
    }  
}

简单注释代码中都有,代码也是之前网上爬虫到的,里边有一些不是很清楚。“${}”或者“#{}”都只是占位符在代码中文本替换作为key来替换。测试的时候发现单元格中的段落在纯文本处理的时候,getpargraph()并没有取到。所以在获取单元格后再次去获取段落内容进行替换,或者修改。时间有限,功底较浅,望各路神仙指点

----2018/04/13

 类似资料: