依賴
<!-- jsoup依賴 主要是解析圖片標(biāo)簽,然后縮放圖片大小-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.12.1</version>
</dependency>
<!-- poi依賴-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.0</version>
</dependency>
ExportController
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.util.IdUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
@RestController
public class ExportController {
@SaIgnore
@PostMapping(value = "/fwbToWord")
public void export(@RequestBody Fwb fwb,
HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
String tmpContent = fwb.getContent();
// 獲取img圖片標(biāo)簽
// 1.Jsoup解析html
Document document = Jsoup.parse(tmpContent);
// 獲取所有img圖片標(biāo)簽
Elements img = document.getElementsByTag("img");
int index = 0;
List<String> imgBase64List = new ArrayList<>();
for (Element element : img) {
imgBase64List.add(element.attr("src"));
// 處理特殊符號
String attrData = element.attr("src");
// base64編碼后可能包含 + 特殊字符,所以需要轉(zhuǎn)義
attrData = attrData.replaceAll("\\+", "\\\\+");
tmpContent = tmpContent.replaceAll(attrData, "{{image_src" + index + "}}");
index++;
}
// 縮放圖片大小,然后重新base64編碼后替換到富文本內(nèi)容里面導(dǎo)出word
index = 0;
String prefix = "data:image/png;base64,"; // base64編碼前綴
for (String base64 : imgBase64List) {
if (StringUtils.isNotBlank(base64)) {
// 縮小圖片
base64 = base64.replaceAll(prefix, "");
BufferedImage bufferedImage = ImageUtils.bytesToBufferedImage(ImageUtils.base64ToByte(base64));
if (bufferedImage == null) {
tmpContent = tmpContent.replaceAll("\\{\\{image_src" + index + "}}", "");
} else {
int height = bufferedImage.getHeight();
int width = bufferedImage.getWidth();
// 如果圖片寬度大于650,圖片縮放
//System.out.println("----"+width+"-----"+height);
if (width > 650) {
//高度等比縮放
height = (int) (height * 650.0 / width);
BufferedImage imgZoom = ImageUtils.resizeImage(bufferedImage, 700, height);
String imageToBase64 = ImageUtils.imageToBase64(ImageUtils.imageToBytes(imgZoom));
tmpContent = tmpContent.replaceAll("\\{\\{image_src" + index + "}}", prefix + imageToBase64);
} else {
tmpContent = tmpContent.replaceAll("\\{\\{image_src" + index + "}}", prefix + base64);
}
}
} else {
tmpContent = tmpContent.replaceAll("\\{\\{image_src" + index + "}}", "");
}
index++;
}
String uuid = IdUtil.fastSimpleUUID();
// 執(zhí)行導(dǎo)出操作
WordUtil.exportHtmlToWord(request, response, tmpContent, uuid);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 倒入本地測試
*
* @throws Exception
*/
@RequestMapping(value = "/export")
public void export() throws Exception {
try {
StringBuilder sb = new StringBuilder("<h1>如何將富文本內(nèi)容導(dǎo)出到word文檔</h1><p style=\"color:red;font-size:20px;\">采用poi將富文本內(nèi)容導(dǎo)出到word文檔</p><div style=\"background-color:green;\">這是有背景顏色的div內(nèi)容</div>\n" +
"<img src=\"").append("");
//提前轉(zhuǎn)換過base64編碼的圖片,由于編碼文本太長,我提前存儲到txt中,再通過io流讀取出來
File file = new File("/Users/lei/base.txt");
FileInputStream fileInputStream = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fileInputStream);
byte[] bytes = new byte[1024];
int len = -1;
while ((len = bis.read(bytes)) != -1) {
sb.append(new String(bytes, 0, len));
}
sb.append("\">這是base64編碼后的圖片");
String tmpContent = sb.toString();
// 獲取img圖片標(biāo)簽
// 1.Jsoup解析html
Document document = Jsoup.parse(tmpContent);
// 獲取所有img圖片標(biāo)簽
Elements imgs = document.getElementsByTag("img");
int index = 0;
List<String> imgBase64List = new ArrayList<>();
for (Element element : imgs) {
imgBase64List.add(element.attr("src"));
// 處理特殊符號
String attrData = element.attr("src");
// base64編碼后可能包含 + 特殊字符,所以需要轉(zhuǎn)義
attrData = attrData.replaceAll("\\+", "\\\\+");
tmpContent = tmpContent.replaceAll(attrData, "{{image_src" + index + "}}");
index++;
}
// 縮放圖片大小,然后重新base64編碼后替換到富文本內(nèi)容里面導(dǎo)出word
index = 0;
String prefix = "data:image/png;base64,"; // base64編碼前綴
for (String base64 : imgBase64List) {
if (StringUtils.isNotBlank(base64)) {
// 縮小圖片
base64 = base64.replaceAll(prefix, "");
BufferedImage bufferedImage = ImageUtils.bytesToBufferedImage(ImageUtils.base64ToByte(base64));
if (bufferedImage == null) {
tmpContent = tmpContent.replaceAll("\\{\\{image_src" + index + "}}", "");
} else {
int height = bufferedImage.getHeight();
int width = bufferedImage.getWidth();
// 如果圖片寬度大于650,圖片縮放
System.out.println("----" + width + "-----" + height);
if (width > 650) {
//高度等比縮放
height = (int) (height * 650.0 / width);
BufferedImage imgZoom = ImageUtils.resizeImage(bufferedImage, 650, height);
String imageToBase64 = ImageUtils.imageToBase64(ImageUtils.imageToBytes(imgZoom));
tmpContent = tmpContent.replaceAll("\\{\\{image_src" + index + "}}", prefix + imageToBase64);
} else {
tmpContent = tmpContent.replaceAll("\\{\\{image_src" + index + "}}", prefix + base64);
}
}
} else {
tmpContent = tmpContent.replaceAll("\\{\\{image_src" + index + "}}", "");
}
index++;
}
String uuid = IdUtil.fastSimpleUUID();
// 執(zhí)行導(dǎo)出操作
WordUtil.exportHtmlToWord("/data", tmpContent, uuid+".docx");
} catch (Exception e) {
e.printStackTrace();
}
}
}
WordUtil
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
public class WordUtil {
/**
* 導(dǎo)出富文本內(nèi)容到word
* @param request
* @param response
* @param content 輸出內(nèi)容
* @param fileName 導(dǎo)出文件名稱
* @throws Exception
*/
public static void exportHtmlToWord(HttpServletRequest request, HttpServletResponse response, String content, String fileName) throws Exception {
// 拼接html格式內(nèi)容
StringBuffer sbf = new StringBuffer();
// 這里拼接一下html標(biāo)簽,便于word文檔能夠識別
sbf.append("<html " +
"xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\" xmlns:w=\"urn:schemas-microsoft-com:office:word\" xmlns:m=\"http://schemas.microsoft.com/office/2004/12/omml\" xmlns=\"http://www.w3.org/TR/REC-html40\"" + //將版式從web版式改成頁面試圖
">");
sbf.append("<head>" +
"<!--[if gte mso 9]><xml><w:WordDocument><w:View>Print</w:View><w:TrackMoves>false</w:TrackMoves><w:TrackFormatting/><w:ValidateAgainstSchemas/><w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid><w:IgnoreMixedContent>false</w:IgnoreMixedContent><w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText><w:DoNotPromoteQF/><w:LidThemeOther>EN-US</w:LidThemeOther><w:LidThemeAsian>ZH-CN</w:LidThemeAsian><w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript><w:Compatibility><w:BreakWrappedTables/><w:SnapToGridInCell/><w:WrapTextWithPunct/><w:UseAsianBreakRules/><w:DontGrowAutofit/><w:SplitPgBreakAndParaMark/><w:DontVertAlignCellWithSp/><w:DontBreakConstrainedForcedTables/><w:DontVertAlignInTxbx/><w:Word11KerningPairs/><w:CachedColBalance/><w:UseFELayout/></w:Compatibility><w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel><m:mathPr><m:mathFont m:val=\"Cambria Math\"/><m:brkBin m:val=\"before\"/><m:brkBinSub m:val=\"--\"/><m:smallFrac m:val=\"off\"/><m:dispDef/><m:lMargin m:val=\"0\"/> <m:rMargin m:val=\"0\"/><m:defJc m:val=\"centerGroup\"/><m:wrapIndent m:val=\"1440\"/><m:intLim m:val=\"subSup\"/><m:naryLim m:val=\"undOvr\"/></m:mathPr></w:WordDocument></xml><![endif]-->" +
"</head>");
sbf.append("<body>");
// 富文本內(nèi)容
sbf.append(content);
sbf.append("</body></html>");
// 必須要設(shè)置編碼,避免中文就會亂碼
byte[] b = sbf.toString().getBytes("GBK");
// 將字節(jié)數(shù)組包裝到流中
ByteArrayInputStream bais = new ByteArrayInputStream(b);
POIFSFileSystem poifs = new POIFSFileSystem();
DirectoryEntry directory = poifs.getRoot();
// 這代碼不能省略,否則導(dǎo)出亂碼。
DocumentEntry documentEntry = directory.createDocument("WordDocument", bais);
//輸出文件
request.setCharacterEncoding("utf-8");
// 導(dǎo)出word格式
response.setContentType("application/msword");
response.addHeader("Content-Disposition", "attachment;filename=" +
new String(fileName.getBytes("GB2312"),"iso8859-1") + ".doc");
ServletOutputStream ostream = response.getOutputStream();
poifs.writeFilesystem(ostream);
bais.close();
ostream.close();
}
/**
* 富文本內(nèi)容到word(本地)
* @param content 輸出內(nèi)容
* @param fileName 導(dǎo)出文件名稱
* @throws Exception
*/
public static void exportHtmlToWord(String filepath, String content, String fileName) throws Exception {
// 拼接html格式內(nèi)容
StringBuffer sbf = new StringBuffer();
// 這里拼接一下html標(biāo)簽,便于word文檔能夠識別
sbf.append("<html " +
"xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\" xmlns:w=\"urn:schemas-microsoft-com:office:word\" xmlns:m=\"http://schemas.microsoft.com/office/2004/12/omml\" xmlns=\"http://www.w3.org/TR/REC-html40\"" + //將版式從web版式改成頁面試圖
">");
sbf.append("<head>" +
"<!--[if gte mso 9]><xml><w:WordDocument><w:View>Print</w:View><w:TrackMoves>false</w:TrackMoves><w:TrackFormatting/><w:ValidateAgainstSchemas/><w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid><w:IgnoreMixedContent>false</w:IgnoreMixedContent><w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText><w:DoNotPromoteQF/><w:LidThemeOther>EN-US</w:LidThemeOther><w:LidThemeAsian>ZH-CN</w:LidThemeAsian><w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript><w:Compatibility><w:BreakWrappedTables/><w:SnapToGridInCell/><w:WrapTextWithPunct/><w:UseAsianBreakRules/><w:DontGrowAutofit/><w:SplitPgBreakAndParaMark/><w:DontVertAlignCellWithSp/><w:DontBreakConstrainedForcedTables/><w:DontVertAlignInTxbx/><w:Word11KerningPairs/><w:CachedColBalance/><w:UseFELayout/></w:Compatibility><w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel><m:mathPr><m:mathFont m:val=\"Cambria Math\"/><m:brkBin m:val=\"before\"/><m:brkBinSub m:val=\"--\"/><m:smallFrac m:val=\"off\"/><m:dispDef/><m:lMargin m:val=\"0\"/> <m:rMargin m:val=\"0\"/><m:defJc m:val=\"centerGroup\"/><m:wrapIndent m:val=\"1440\"/><m:intLim m:val=\"subSup\"/><m:naryLim m:val=\"undOvr\"/></m:mathPr></w:WordDocument></xml><![endif]-->" +
"</head>");
sbf.append("<body>");
// 富文本內(nèi)容
sbf.append(content);
sbf.append("</body></html>");
// 必須要設(shè)置編碼,避免中文就會亂碼
byte[] b = sbf.toString().getBytes("GBK");
// 將字節(jié)數(shù)組包裝到流中
ByteArrayInputStream bais = new ByteArrayInputStream(b);
POIFSFileSystem poifs = new POIFSFileSystem();
DirectoryEntry directory = poifs.getRoot();
// 這代碼不能省略,否則導(dǎo)出亂碼。
DocumentEntry documentEntry = directory.createDocument("WordDocument", bais);
FileOutputStream out = new FileOutputStream(new File(filepath+fileName));
poifs.writeFilesystem(out);
bais.close();
out.close();
}
}
ImageUtils
import cn.hutool.core.codec.Base64Decoder;
import cn.hutool.core.codec.Base64Encoder;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class ImageUtils {
/**
* 通過BufferedImage圖片流調(diào)整圖片大小
*/
public static BufferedImage resizeImage(BufferedImage originalImage, int targetWidth, int targetHeight) throws IOException {
Image resultingImage = originalImage.getScaledInstance(targetWidth, targetHeight, Image.SCALE_AREA_AVERAGING);
BufferedImage outputImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
outputImage.getGraphics().drawImage(resultingImage, 0, 0, null);
return outputImage;
}
/**
* 返回base64圖片
* @param data
* @return
*/
public static String imageToBase64(byte[] data) {
// 返回Base64編碼過的字節(jié)數(shù)組字符串
return Base64Encoder.encode(data);
}
/**
* base64轉(zhuǎn)換成byte數(shù)組
* @param base64
* @return
* @throws IOException
*/
public static byte[] base64ToByte(String base64) throws IOException {
// 返回Base64編碼過的字節(jié)數(shù)組字符串
return Base64Decoder.decode(base64);
}
/**
* BufferedImage圖片流轉(zhuǎn)byte[]數(shù)組
*/
public static byte[] imageToBytes(BufferedImage bImage) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
ImageIO.write(bImage, "png", out);
} catch (IOException e) {
e.printStackTrace();
}
return out.toByteArray();
}
/**
* byte[]數(shù)組轉(zhuǎn)BufferedImage圖片流
*/
public static BufferedImage bytesToBufferedImage(byte[] ImageByte) {
ByteArrayInputStream in = new ByteArrayInputStream(ImageByte);
BufferedImage image = null;
try {
image = ImageIO.read(in);
} catch (IOException e) {
e.printStackTrace();
}
return image;
}
}