优化word文档的导出

This commit is contained in:
sswiki
2024-06-25 19:29:44 +08:00
parent bdeab06d6c
commit f5d32c6114
3 changed files with 152 additions and 55 deletions

View File

@@ -0,0 +1,28 @@
package com.zyplayer.doc.data.utils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HtmlUtils {
private static final Logger logger = LoggerFactory.getLogger(HtmlUtils.class);
/**
* 从Content-Disposition头部提取文件名
*
* @param contentDisposition Content-Disposition头部字符串
* @return 文件名如果未找到则返回null
*/
public static String extractFilename(String contentDisposition, String defaultName) {
if (StringUtils.isBlank(contentDisposition)) {
return defaultName;
}
for (String name : contentDisposition.split(";")) {
String[] nameArr = name.trim().split("=");
if (nameArr.length >= 2 && StringUtils.startsWith(nameArr[0], "filename")) {
return StringUtils.removeEnd(StringUtils.removeStart(nameArr[1].trim(), "\""), "\"");
}
}
return defaultName;
}
}

View File

@@ -29,6 +29,7 @@ import com.zyplayer.doc.wiki.controller.vo.WikiPageContentVo;
import com.zyplayer.doc.wiki.controller.vo.WikiPageVo;
import com.zyplayer.doc.wiki.framework.consts.SpaceType;
import com.zyplayer.doc.wiki.service.WikiPageUploadService;
import com.zyplayer.doc.wiki.service.WikiPageWebService;
import com.zyplayer.doc.wiki.service.common.WikiPageAuthService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -84,7 +85,7 @@ public class WikiPageController {
private final WikiPageMapper wikiPageMapper;
private final WikiPageCommentService wikiPageCommentService;
private final WikiPageTemplateService wikiPageTemplateService;
private final WikiPageWebService wikiPageWebService;
@PostMapping("/list")
public ResponseJson<List<WikiPageVo>> list(WikiPage wikiPage) {
@@ -429,60 +430,7 @@ public class WikiPageController {
@PostMapping("/download")
public ResponseJson<Object> download(Long pageId, String content, HttpServletRequest request, HttpServletResponse response) {
DocUserDetails currentUser = DocUserUtil.getCurrentUser();
String requestURI = request.getRequestURL().toString();
WikiPage wikiPageSel = wikiPageService.getById(pageId);
// 页面已删除
if (wikiPageSel == null || Objects.equals(wikiPageSel.getDelFlag(), 1)) {
return DocResponseJson.warn("该页面不存在或已删除!");
}
WikiSpace wikiSpaceSel = wikiSpaceService.getById(wikiPageSel.getSpaceId());
// 空间已删除
if (wikiSpaceSel == null || Objects.equals(wikiSpaceSel.getDelFlag(), 1)) {
return DocResponseJson.warn("该页面不存在或已删除!");
}
// 私人空间
if (SpaceType.isOthersPrivate(wikiSpaceSel.getType(), currentUser.getUserId(), wikiSpaceSel.getCreateUserId())) {
return DocResponseJson.warn("您没有权限查看该空间的文章详情!");
}
try {
String fileName = URLEncoder.encode(wikiPageSel.getName(), "UTF-8").replaceAll("\\+", "%20");
String domainUri = requestURI.substring(0, requestURI.indexOf("/zyplayer-doc-wiki") + 1);
// 解析内容并替换图片URL
Document document = Jsoup.parse(content);
document.outputSettings().syntax(Document.OutputSettings.Syntax.xml).escapeMode(Entities.EscapeMode.xhtml);
Elements images = document.select("img");
for (Element image : images) {
image.attr("src", domainUri + image.attr("src"));
}
content = document.html();
content = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Strict//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n" +
"<html lang=\"zh\">\n" +
"<head>\n" +
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n" +
"<title>" + fileName + "</title>\n" +
"</head>\n" +
"<body>" +
content +
"</body>\n" +
"</html>";
// 写入流
response.setCharacterEncoding("utf-8");
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".docx");
ServletOutputStream outputStream = response.getOutputStream();
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
MainDocumentPart mdp = wordMLPackage.getMainDocumentPart();
mdp.addAltChunk(AltChunkType.Xhtml, content.getBytes(StandardCharsets.UTF_8));
mdp.convertAltChunks();
XmlUtils.marshaltoString(wordMLPackage.getMainDocumentPart().getJaxbElement(), true, true);
wordMLPackage.save(outputStream);
outputStream.close();
return DocResponseJson.ok();
} catch (Exception e) {
e.printStackTrace();
}
return DocResponseJson.warn("导出失败");
return wikiPageWebService.download(pageId, content, request, response);
}
@PostMapping("/news")

View File

@@ -0,0 +1,121 @@
package com.zyplayer.doc.wiki.service;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.system.SystemUtil;
import com.zyplayer.doc.core.json.DocResponseJson;
import com.zyplayer.doc.core.json.ResponseJson;
import com.zyplayer.doc.data.config.security.DocUserDetails;
import com.zyplayer.doc.data.config.security.DocUserUtil;
import com.zyplayer.doc.data.repository.manage.entity.WikiPage;
import com.zyplayer.doc.data.repository.manage.entity.WikiSpace;
import com.zyplayer.doc.data.service.manage.WikiPageService;
import com.zyplayer.doc.data.service.manage.WikiSpaceService;
import com.zyplayer.doc.data.utils.HtmlUtils;
import com.zyplayer.doc.wiki.framework.consts.SpaceType;
import org.apache.commons.lang3.StringUtils;
import org.docx4j.XmlUtils;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.AltChunkType;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Entities;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
@Service
public class WikiPageWebService {
private static final Logger logger = LoggerFactory.getLogger(WikiPageWebService.class);
@Resource
WikiPageService wikiPageService;
@Resource
WikiSpaceService wikiSpaceService;
public ResponseJson<Object> download(Long pageId, String content, HttpServletRequest request, HttpServletResponse response) {
DocUserDetails currentUser = DocUserUtil.getCurrentUser();
String requestURI = request.getRequestURL().toString();
WikiPage wikiPageSel = wikiPageService.getById(pageId);
// 页面已删除
if (wikiPageSel == null || Objects.equals(wikiPageSel.getDelFlag(), 1)) {
return DocResponseJson.warn("该页面不存在或已删除!");
}
WikiSpace wikiSpaceSel = wikiSpaceService.getById(wikiPageSel.getSpaceId());
// 空间已删除
if (wikiSpaceSel == null || Objects.equals(wikiSpaceSel.getDelFlag(), 1)) {
return DocResponseJson.warn("该页面不存在或已删除!");
}
// 私人空间
if (SpaceType.isOthersPrivate(wikiSpaceSel.getType(), currentUser.getUserId(), wikiSpaceSel.getCreateUserId())) {
return DocResponseJson.warn("您没有权限查看该空间的文章详情!");
}
String srcFilePath = new File(SystemUtil.get("java.io.tmpdir"), IdUtil.objectId()).getPath();
try {
String fileName = URLEncoder.encode(wikiPageSel.getName(), "UTF-8").replaceAll("\\+", "%20");
String domainUri = requestURI.substring(0, requestURI.indexOf("/zyplayer-doc-wiki") + 1);
// 解析内容并替换图片URL
Document document = Jsoup.parse(content);
document.outputSettings().syntax(Document.OutputSettings.Syntax.xml).escapeMode(Entities.EscapeMode.xhtml);
Elements images = document.select("img");
for (Element image : images) {
String src = image.attr("src");
if (StringUtils.startsWith(src, "zyplayer-doc-wiki/common/file?uuid=")) {
src = domainUri + src;
}
try {
HttpResponse execute = HttpUtil.createGet(src).timeout(5000).execute();
String originFileName = HtmlUtils.extractFilename(execute.header("Content-Disposition"), "image.png");
String extName = FileNameUtil.extName(originFileName);
File srcFile = new File(srcFilePath, IdUtil.objectId() + "." + extName);
FileUtil.writeFromStream(execute.bodyStream(), srcFile);
image.attr("src", URLUtil.getURL(srcFile).toString());
} catch (Exception e) {
image.attr("src", "");
logger.error("下载图片失败,忽略异常:{}", e.getMessage());
}
}
content = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Strict//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n" +
"<html lang=\"zh\">\n" +
"<head>\n" +
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n" +
"</head>\n" +
"<body>" + document.body().html() + "</body>\n" +
"</html>";
// 写入流
response.setCharacterEncoding("utf-8");
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".docx");
ServletOutputStream outputStream = response.getOutputStream();
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
MainDocumentPart mdp = wordMLPackage.getMainDocumentPart();
mdp.addAltChunk(AltChunkType.Xhtml, content.getBytes(StandardCharsets.UTF_8));
mdp.convertAltChunks();
XmlUtils.marshaltoString(wordMLPackage.getMainDocumentPart().getJaxbElement(), true, true);
wordMLPackage.save(outputStream);
outputStream.close();
return DocResponseJson.ok();
} catch (Exception e) {
logger.error("下载文件失败", e);
} finally {
FileUtil.del(srcFilePath);
}
return DocResponseJson.warn("导出失败");
}
}