4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

POI を使い、WORD(docx)テンプレートを更新・出力する。

Last updated at Posted at 2019-08-09

Spring Boot2 と Apache POI を使い、 Word(docx)のテンプレートファイル内のテキストボックス内のテキスト、パラグラフ中のテキストを置換したファイルをダウンロードするサンプル。
localhost:8080/download/word へPOSTするとWordファイルのダウンロードが始まる。

XlsMapper のような処理をやりたかったが、Wordファイル内のテンプレートキーとして入力された文字が、文字種類で分割されるなど容易に置換できそうになかったので、力技で。

参考:https://codeday.me/jp/qa/20190129/191063.html

DownloadFilesController.java
	@Autowired
	WordBuilder wordBuilder;
	
	
    @PostMapping("/download/word")
    public ModelAndView  downloadWord(@RequestParam Map<String, String> requestMap) {
    	
    	ModelAndView mav = new ModelAndView(wordBuilder);

        return mav;
    	
    }
WordBuilder.java
package jp.co.core.kenkei.view.sample.builder;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.xwpf.usermodel.IRunBody;
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;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.view.AbstractView;

import lombok.extern.log4j.Log4j2;

@Log4j2
@Component
public class WordBuilder extends AbstractView {

	@Autowired
	private TemplateConfiguration configuration;

	private XWPFDocument createXWPFDocument() {

		File wordTemplateFile = new File(configuration.getWordTemplate());

		XWPFDocument workbook = null;
		try (

				InputStream is = new ByteArrayInputStream(

						Files.readAllBytes(wordTemplateFile.toPath()));

		) {

			workbook = new XWPFDocument(is);

		} catch (IOException | EncryptedDocumentException e) {
			log.error("create workbook error", e);

		}

		return workbook;
	}

	private void outputDocx(XWPFDocument doc) {

		for (XWPFParagraph p : doc.getParagraphs()) {
			List<XWPFRun> runs = p.getRuns();
			if (runs != null) {
				for (XWPFRun r : runs) {
					String text = r.getText(0);
					System.out.println("--- text=" + text);
					if (text != null && text.contains("あいさつ")) {
						text = text.replace("あいさつ", "リプレイス-HELLO");
						r.setText(text, 0);
					}
				}
			}
		}
		for (XWPFTable tbl : doc.getTables()) {
			for (XWPFTableRow row : tbl.getRows()) {
				for (XWPFTableCell cell : row.getTableCells()) {
					for (XWPFParagraph p : cell.getParagraphs()) {
						for (XWPFRun r : p.getRuns()) {
							String text = r.getText(0);
							System.out.println("TTT text=" + text);
							if (text.contains("needle")) {
								text = text.replace("needle", "haystack");
								r.setText(text);
							}
						}
					}
				}
			}
		}
	}

	private void replaceTextBox(XWPFDocument doc, String word1, String word2) throws XmlException {

		for (XWPFParagraph paragraph : doc.getParagraphs()) {
			XmlCursor cursor = paragraph.getCTP().newCursor();
			cursor.selectPath(
					"declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' .//*/w:txbxContent/w:p/w:r");

			List<XmlObject> ctrsintxtbx = new ArrayList<XmlObject>();

			while (cursor.hasNextSelection()) {
				cursor.toNextSelection();
				XmlObject obj = cursor.getObject();
				ctrsintxtbx.add(obj);
			}
			
			for (XmlObject obj : ctrsintxtbx) {
				
				CTR ctr = CTR.Factory.parse(obj.xmlText());
				
				// CTR ctr = CTR.Factory.parse(obj.newInputStream());
				XWPFRun bufferrun = new XWPFRun(ctr, (IRunBody) paragraph);
				
				String text = bufferrun.getText(0);
				System.out.println("::::: " + text);
				
				if (text != null && text.contains("文字壱") ) {
					text = text.replace(text,   text + word1 );
					bufferrun.setText(text, 0);
					obj.set(bufferrun.getCTR());
					continue;
				}
				
				if (text != null && text.contains("文字弐") ) {
					text = text.replace(text,   text + word2 );
					bufferrun.setText(text, 0);
					obj.set(bufferrun.getCTR());
					continue;
				}
				
				if(text == null) {
					continue;
				}
				
				text = text.replace(text,  "replaced:" + text );
				bufferrun.setText(text, 0);
				obj.set(bufferrun.getCTR());
			}
		}

//		  document.write(new FileOutputStream("WordReplaceTextInTextBoxNew.docx"));
//		  document.close();

	}

	@Override
	protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
			HttpServletResponse response) throws Exception {

		final String encodedFilename = URLEncoder.encode("Wordダウンロード:サンプル.docx", "UTF-8");
		response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFilename);

		XWPFDocument document = createXWPFDocument();

		outputDocx(document);
		replaceTextBox(document, request.getParameter("word1"), request.getParameter("word2") );

		response.setContentType("application/msword");

		document.write(response.getOutputStream());
		if (document != null) {
			document.close();
		}

	}

}


4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?