java如何替換word/doc文件中的內(nèi)容
docx格式的文件本質(zhì)上是一個(gè)XML文件,只要用占位符在指定的地方標(biāo)記,然后替換掉標(biāo)記出的內(nèi)容,就能達(dá)到我們的目的

封裝成工具類
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* Author ht
* Date: 2023-06-30
*/
//替換.docx文件的工具類
public class DocxUtil {
// 保存隨機(jī)生的解壓出.docx文件的保存目錄
private static final String cachePath = System.getProperty("user.dir") + File.separator + UUID.randomUUID().toString().replaceAll("-", "");
// 保存模板文件的輸入流和模板壓縮輸入流
private static final List<InputStream> iss = new ArrayList<>();
// 替換docx文件中的內(nèi)容
public static void compile(FileInputStream is, Map<String, String> data, FileOutputStream os) throws IOException {
// 獲取模板保存的目錄
Path path = Paths.get(cachePath);
// 如果不存在或者當(dāng)前的模板輸入流不在輸入流集合中就解壓模板
if (!Files.exists(path) || !iss.contains(is)) {
// 解壓docx文件
unDocx(is);
}
// 替換document.xml的內(nèi)容
replaceXmlContent(data);
// 把解壓的文件重新壓縮成docx文件
buildDocx(os);
}
// 解壓docx文件
private static void unDocx(FileInputStream template) throws IOException {
// 解壓出來(lái)文件保存的目錄
File cacheDir = new File(cachePath);
if (!cacheDir.exists()) {
// 不存在就先創(chuàng)建該文件夾
Files.createDirectory(Paths.get(cacheDir.getPath()));
}
// 創(chuàng)建壓縮包輸入流
ZipInputStream zis = new ZipInputStream(template);
// 把需要關(guān)閉的模板輸入流存到集合中
iss.add(template);
iss.add(zis);
// 獲取首個(gè)壓縮文件中的文件
ZipEntry entry = zis.getNextEntry();
// 內(nèi)容緩沖區(qū)
byte[] buff = new byte[1024 * 10];
// 循環(huán)獲取壓縮包中的壓縮文件
while (entry != null) {
// 判斷當(dāng)前的壓縮文件是文件夾還是文件
if (entry.isDirectory()) {
// 如果是文件夾就要?jiǎng)?chuàng)建對(duì)應(yīng)的文件夾
Path tempDir = Paths.get(cacheDir.getPath() + File.separator + entry.getName());
// 不存在就創(chuàng)建
if (!Files.exists(tempDir)) {
Files.createDirectory(tempDir);
}
} else {
// 如果是文件就創(chuàng)建文件寫入內(nèi)容
File file = new File(cacheDir, entry.getName());
// 創(chuàng)建文件的輸出流
FileOutputStream fos = new FileOutputStream(file);
// 寫入內(nèi)容到輸出流
int len;
while ((len = zis.read(buff)) != -1) {
fos.write(buff, 0, len);
}
// zis.transferTo(fos); // jdk9及更高版本可以用這個(gè)
// 關(guān)閉當(dāng)前文件的輸出流
fos.close();
}
// 獲取下一個(gè)壓縮文件
entry = zis.getNextEntry();
}
System.out.println("===== 模板docx文件解壓完成 =====");
}
// 替換document.xml的內(nèi)容
private static void replaceXmlContent(Map<String, String> data) throws IOException {
// document.xml的位置
String documentXmlPath = cachePath + File.separator + "word" + File.separator + "document.xml";
// 獲取document.xml文件的輸入流
FileInputStream fis = new FileInputStream(documentXmlPath);
// 讀取xml中的內(nèi)容
byte[] buff = new byte[fis.available()];
fis.read(buff);
// 獲取xml文件中的內(nèi)容
String content = new String(buff);
// 拿到Map集合中的數(shù)據(jù)和值,替換掉對(duì)應(yīng)位置的內(nèi)容
for (String key : data.keySet()) {
String value = data.get(key);
System.out.println("替換 [ {{" + key + "}} ] 為 => [ " + value + " ]");
content = content.replaceAll("\\{\\{" + key + "\\}\\}", value);
}
// 把替換好的內(nèi)容寫入原來(lái)的document.xml文件中
FileOutputStream fos = new FileOutputStream(documentXmlPath);
// 寫入內(nèi)容
fos.write(content.getBytes(StandardCharsets.UTF_8));
// 關(guān)閉流
fos.close();
fis.close();
System.out.println("===== 替換完成 =====");
}
// 把替換好document.xml內(nèi)容的文件夾重新壓縮成docx文件
private static void buildDocx(FileOutputStream template) throws IOException {
// 創(chuàng)建壓縮文件輸出流
ZipOutputStream zos = new ZipOutputStream(new DataOutputStream(template));
// 獲取要壓縮文件的路徑
Path path = Paths.get(cachePath);
// 遍歷當(dāng)前壓縮文件路徑下的所有文件
Files.walkFileTree(path, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
// 獲取當(dāng)前文件的父目錄的名字
String parent = file.getParent().getFileName().toString();
// 壓縮文件的名字
String zipFileName;
// 如果父目錄是根目錄名字就是當(dāng)前的名字否則就是夫目錄加上當(dāng)前的名字
if (parent.equals(path.toFile().getName())) {
zipFileName = file.toFile().getName();
} else {
// 如果父目錄的夫目錄是word就需要word+當(dāng)前文件的父目錄+當(dāng)前文件的名字
if ("word".equals(file.getParent().getParent().toFile().getName())) {
zipFileName = "word" + File.separator + parent + File.separator + file.toFile().getName();
} else {
// 如果不是就是當(dāng)前文件的父目錄+當(dāng)前文件的名字
zipFileName = parent + File.separator + file.toFile().getName();
}
}
// 把文件添加到壓縮包中
zos.putNextEntry(new ZipEntry(zipFileName));
// 獲取當(dāng)前文件的輸入流
DataInputStream zis = new DataInputStream(new FileInputStream(file.toFile()));
// 寫入內(nèi)容到當(dāng)前的壓縮文件
int len;
byte[] buff = new byte[1024 * 10];
while ((len = zis.read(buff)) != -1) {
zos.write(buff, 0, len);
}
zis.close();
// zis.transferTo(zos); // jdk9及更高版本可以用這個(gè)
return super.visitFile(file, attrs);
}
});
// 關(guān)閉流
zos.close();
template.close();
System.out.println("===== 把解壓的文件重新壓縮成.docx ====");
}
// 關(guān)閉模板輸入流和清除緩存文件
public static void closeTemplateAndClearCache() {
// 關(guān)閉集合中的所有流
for (InputStream inputStream : iss) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 刪除緩存文件
deleteCache(new File(cachePath));
System.out.println("===== 關(guān)閉了模板,清理了緩存 =====");
}
// 刪除不用的緩存解壓文件目錄
private static void deleteCache(File cachePath) {
// 先刪文件夾里面的文件
for (File file : Objects.requireNonNull(cachePath.listFiles())) {
// 是文件夾就遞歸調(diào)用
if (file.isDirectory()) {
deleteCache(file);
} else {
// 是文件就直接刪
file.delete();
}
}
// 刪掉自己
cachePath.delete();
}
}使用
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* Author ht
* Date: 2023-06-30
*/
public class App {
public static void main(String[] args) throws IOException {
// 填充的數(shù)據(jù)
Map<String, String> data = new HashMap<>();
data.put("name", "hello world");
data.put("time", "2022年10月1日");
// 模板輸入流
FileInputStream is = new FileInputStream("src/main/resources/one.docx");
// 模板輸出流
FileOutputStream os = new FileOutputStream("src/main/resources/test1.docx");
FileOutputStream os1 = new FileOutputStream("src/main/resources/test2.docx");
// 生成模板
DocxUtil.compile(is, data, os);
DocxUtil.compile(is, data, os1);
// 關(guān)閉模板輸入流和清除緩存文件
DocxUtil.closeTemplateAndClearCache();
}
}記錄一些代碼和問(wèn)題處理方式,需要參考的請(qǐng)謹(jǐn)慎。
到此這篇關(guān)于java如何替換word/doc文件中的內(nèi)容的文章就介紹到這了,更多相關(guān)java替換文件內(nèi)容內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 高并發(fā)的三種實(shí)現(xiàn)案例詳解
這篇文章主要介紹了Java 高并發(fā)的三種實(shí)現(xiàn)案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
MyBatis實(shí)現(xiàn)兩種查詢樹形數(shù)據(jù)的方法詳解(嵌套結(jié)果集和遞歸查詢)
樹形結(jié)構(gòu)數(shù)據(jù)在開發(fā)中十分常見,比如:菜單數(shù)、組織樹, 利用 MyBatis 提供嵌套查詢功能可以很方便地實(shí)現(xiàn)這個(gè)功能需求。本文主要介紹了兩種方法,感興趣的可以了解一下2021-09-09
Java創(chuàng)建線程的七種方法總結(jié)(全網(wǎng)最全面)
線程是Java中的基本執(zhí)行單元,它允許程序在同一時(shí)間執(zhí)行多個(gè)任務(wù),下面這篇文章主要給大家總結(jié)介紹了關(guān)于Java創(chuàng)建線程的七種方法,文中通過(guò)實(shí)例代碼將這七種方法介紹的非常詳細(xì),需要的朋友可以參考下2023-05-05
Java調(diào)用基于Ollama本地大模型的實(shí)現(xiàn)
本文主要介紹了Java調(diào)用基于Ollama本地大模型的實(shí)現(xiàn),實(shí)現(xiàn)文本生成、問(wèn)答、文本分類等功能,開發(fā)者可以輕松配置和調(diào)用模型,具有一定的參考價(jià)值,感興趣的可以了解一下2025-03-03
淺談Java?abstract關(guān)鍵字不能和哪些關(guān)鍵字共存
本文主要介紹了Java?abstract關(guān)鍵字不能和哪些關(guān)鍵字共存,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-10-10
java實(shí)現(xiàn)請(qǐng)求緩沖合并的示例代碼
我們對(duì)外提供了一個(gè)rest接口給第三方業(yè)務(wù)進(jìn)行調(diào)用,但是由于第三方框架限制,導(dǎo)致會(huì)發(fā)送大量相似無(wú)效請(qǐng)求,這篇文章主要介紹了java實(shí)現(xiàn)請(qǐng)求緩沖合并,需要的朋友可以參考下2024-04-04
Java?list如何實(shí)現(xiàn)將指定元素排在第一位
這篇文章主要為大家詳細(xì)介紹了Java?list中如何實(shí)現(xiàn)將指定元素排在第一位,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-02-02

