diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index 03a023d..aad287b 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -16,6 +16,41 @@ + + org.jsoup + jsoup + 1.14.3 + + + + + org.apache.httpcomponents + httpcore + 4.4.13 + + + org.springframework + spring-test + 5.2.15.RELEASE + + + + + com.itextpdf + itext-asian + 5.2.0 + + + + com.itextpdf + itextpdf + 5.5.13 + + + com.itextpdf.tool + xmlworker + 5.5.13 + org.projectlombok lombok @@ -68,6 +103,11 @@ knife4j-openapi2-spring-boot-starter 4.4.0 + + com.itextpdf + html2pdf + 1.17.2 + diff --git a/ruoyi-admin/src/main/java/com/ruoyi/gysl/controller/DpclglController.java b/ruoyi-admin/src/main/java/com/ruoyi/gysl/controller/DpclglController.java index 392f0f4..a616b05 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/gysl/controller/DpclglController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/gysl/controller/DpclglController.java @@ -7,10 +7,14 @@ import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.gysl.entity.Dpclgl; import com.ruoyi.gysl.entity.request.DpclglPageReq; import com.ruoyi.gysl.service.DpclglService; +import com.ruoyi.gysl.utils.HtmlToPdfConverter; +import com.ruoyi.gysl.utils.PdfUtils; +import com.ruoyi.web.controller.common.CommonController; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.io.Serializable; @@ -22,11 +26,15 @@ import java.util.List; * @author makejava * @since 2025-03-22 11:11:55 */ -@Api(tags ="单片材料管理" ) +@Api(tags = "单片材料管理") @RestController @RequestMapping("/gysl/dpclgl") -@PreAuthorize("@ss.hasAnyRoles('admin,common')") +//@PreAuthorize("@ss.hasAnyRoles('admin,common')") public class DpclglController extends BaseController { + + + @Resource + private CommonController commonController; /** * 服务对象 */ @@ -36,7 +44,7 @@ public class DpclglController extends BaseController { /** * 分页查询所有数据 * - * @param page 分页对象 + * @param page 分页对象 * @param dpclgl 查询实体 * @return 所有数据 */ @@ -93,5 +101,25 @@ public class DpclglController extends BaseController { public AjaxResult delete(@RequestParam("idList") List idList) { return success(dpclglService.removeByIds(idList)); } + + + /** + * 富文本导出 + * + * @return 新增结果 + */ + @ApiOperation(value = "副编辑器内容生成pdf文件url") + @GetMapping("/unloadPdf") + public AjaxResult unloadPdf(@RequestParam(defaultValue = "文章标题") String htmlArticleTitle, @RequestParam(defaultValue = "文章内容") String htmlContent) { + try { + MultipartFile multipartFile = HtmlToPdfConverter.convertHtmlToPdf(htmlArticleTitle, htmlContent); + commonController.uploadFile(multipartFile); + + } catch (Exception e) { + e.printStackTrace(); + } + return AjaxResult.success(); + } + } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/gysl/utils/HtmlToPdfConverter.java b/ruoyi-admin/src/main/java/com/ruoyi/gysl/utils/HtmlToPdfConverter.java new file mode 100644 index 0000000..5245359 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/gysl/utils/HtmlToPdfConverter.java @@ -0,0 +1,211 @@ +package com.ruoyi.gysl.utils; + +import com.itextpdf.text.Document; +import com.itextpdf.text.DocumentException; +import com.itextpdf.text.Image; +import com.itextpdf.text.Paragraph; +import com.itextpdf.text.pdf.PdfWriter; +import com.itextpdf.tool.xml.XMLWorkerFontProvider; +import com.itextpdf.tool.xml.XMLWorkerHelper; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.web.multipart.MultipartFile; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Date; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class HtmlToPdfConverter { + + public static MultipartFile convertHtmlToPdf(String htmlArticleTitle, String htmlContent) throws IOException { + + String ht = content2Html(htmlContent); + + + String path = System.getProperty("user.dir"); + // 根据日期建文件夹 + String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); + String[] split = format.split("-"); + Path dirPath = Paths.get(path, "temporary", split[0], split[1], split[2]); + try { + Files.createDirectories(dirPath); + } catch (IOException e) { + throw new RuntimeException("创建文件夹失败: " + e.getMessage(), e); + } + File pdfFile = new File(dirPath.toFile(), "pdf-" + System.currentTimeMillis() + ".pdf"); + // 创建PDF文档 + Document document = new Document(); + + FileOutputStream fos = null; + InputStream is = null; + try { + fos = new FileOutputStream(pdfFile); + + PdfWriter writer = PdfWriter.getInstance(document, fos); + XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS); + fontProvider.register("C:/Windows/Fonts/simsun.ttc"); + + document.open(); + // 添加标题 + Paragraph paragraph = new Paragraph(htmlArticleTitle); // 设置你自定义的字体格式 + document.add(paragraph); + String updatedHtmlContent = ""; + if (htmlContent.contains("img")) { + updatedHtmlContent = replaceImgSrcWithBase64(ht); + + if (updatedHtmlContent != null) { + is = new ByteArrayInputStream(updatedHtmlContent.getBytes(StandardCharsets.UTF_8)); + XMLWorkerHelper.getInstance().parseXHtml(writer, document, is, StandardCharsets.UTF_8, fontProvider); + // 添加图片 + Elements imgElements = Jsoup.parse(updatedHtmlContent).select("img"); + for (Element img : imgElements) { + String imgSrc = img.attr("src"); + if (imgSrc.startsWith("data:image/jpeg;base64,")) { + String base64Data = imgSrc.substring("data:image/jpeg;base64,".length()); + byte[] imageBytes = Base64.getDecoder().decode(base64Data); + Image image = Image.getInstance(imageBytes); + document.add(image); // 添加图片到 PDF + } + } + } + + } else { + is = new ByteArrayInputStream(ht.getBytes(StandardCharsets.UTF_8)); + XMLWorkerHelper.getInstance().parseXHtml(writer, document, is, StandardCharsets.UTF_8, fontProvider); + + } + + } catch (DocumentException | IOException e) { + throw new RuntimeException("转换PDF失败: " + e.getMessage(), e); + } finally { + + if (document.isOpen()) { + document.close(); // 确保Document关闭 + } + if (fos != null) { + try { + fos.close(); // 关闭文件输出流 + } catch (IOException e) { + e.printStackTrace(); + } + } + if (is != null) { + try { + is.close(); // 关闭输入流 + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + return getMultipartFile(pdfFile); + } + + private static MultipartFile getMultipartFile(File file) throws IOException { + try (FileInputStream fileInputStream = new FileInputStream(file)) { + return new MockMultipartFile(file.getName(), file.getName(), "application/pdf", fileInputStream); + } + } + + public static String content2Html(String htmlContent) { + String COMPLETE_CONTENT = "" + htmlContent + ""; + return COMPLETE_CONTENT.replace("
", "
"); + } + + + private static String replaceImgSrcWithBase64(String htmlContent) { + List imgSrcs = new ArrayList<>(); + String regex = "]+src=\"([^\"]+)\"[^>]*>"; // 正确的正则表达式 + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(htmlContent); + String baseUrl = "http://39.101.188.84:7071"; + + // 使用 StringBuilder 来构建新的 HTML + StringBuilder updatedHtml = new StringBuilder(htmlContent); + + // 定义要替换的起始位置 + int offset = 0; + + while (matcher.find()) { + String imgSrc = matcher.group(1); // 捕获组返回 img src 的值 + String imgUrl = baseUrl + imgSrc; + + // 将 URL 转化为 Base64 + try { + URL url = new URL(imgUrl); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestProperty("User-Agent", "Mozilla/5.0"); // 添加 User-Agent + connection.setDoInput(true); + connection.connect(); + InputStream inputStream = connection.getInputStream(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + inputStream.close(); + byte[] imageBytes = outputStream.toByteArray(); + String base64 = Base64.getEncoder().encodeToString(imageBytes); + + // 替换 img 标签的 src 属性为 Base64 数据 + String base64ImgTag = ""; + int start = matcher.start() + offset; // 累计偏移量 + int end = matcher.end(); + + // 替换原来的 img 标签 + updatedHtml.replace(start, end, base64ImgTag); + //System.out.println(updatedHtml.toString()); + // 更新偏移量 + offset += base64ImgTag.length() - (end - start); + } catch (Exception e) { + e.printStackTrace(); + return null; // 处理异常或返回特定的错误值 + } + } + return updatedHtml.toString(); + } + + public String convertImageToBase64(String imgUrl) { + try { + // String encodedImgUrl = URLEncoder.encode(imgUrl, StandardCharsets.UTF_8.toString()); + + URL url = new URL(imgUrl); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestProperty("User-Agent", "Mozilla/5.0"); // 添加 User-Agent + connection.setDoInput(true); + connection.connect(); + InputStream inputStream = connection.getInputStream(); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + inputStream.close(); + + byte[] imageBytes = outputStream.toByteArray(); + String base64 = Base64.getEncoder().encodeToString(imageBytes); + return "data:image/jpeg;base64," + base64; // 根据实际情况修改 MIME 类型 + } catch (Exception e) { + e.printStackTrace(); + return null; // 处理异常或返回特定的错误值 + } + } + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/gysl/utils/ImgBase64Extractor.java b/ruoyi-admin/src/main/java/com/ruoyi/gysl/utils/ImgBase64Extractor.java new file mode 100644 index 0000000..3538f36 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/gysl/utils/ImgBase64Extractor.java @@ -0,0 +1,55 @@ +package com.ruoyi.gysl.utils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; + +public class ImgBase64Extractor { + + public static void main(String[] args) throws IOException { + String imgUrl ="http://39.101.188.84:7071/profile/upload/2025/03/24/abc_20250324154129A006.jpg"; + + URL url = new URL(imgUrl); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setDoInput(true); + // 添加请求头 + connection.setRequestProperty("User-Agent", "Mozilla/5.0"); + connection.setRequestProperty("Accept", "image/webp,image/apng,image/*,*/*;q=0.8"); + connection.setRequestProperty("Accept-Language", "en-US,en;q=0.5"); + + // 连接并获取响应 + connection.connect(); + + // 检查响应码 + int responseCode = connection.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_OK) { + // 连接成功,获取输入流 + InputStream inputStream = connection.getInputStream(); + + // 读取输入流 + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int bytesRead; + + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + + inputStream.close(); + byte[] imageBytes = outputStream.toByteArray(); + System.out.println(imageBytes); + } else { + System.out.println("Error: Server returned HTTP response code: " + responseCode); + + } + } + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/gysl/utils/PdfUtils.java b/ruoyi-admin/src/main/java/com/ruoyi/gysl/utils/PdfUtils.java new file mode 100644 index 0000000..ce2589b --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/gysl/utils/PdfUtils.java @@ -0,0 +1,168 @@ +package com.ruoyi.gysl.utils; + + + +import com.itextpdf.text.Document; +import com.itextpdf.text.DocumentException; +import com.itextpdf.text.Font; +import com.itextpdf.text.Paragraph; +import com.itextpdf.text.pdf.BaseFont; +import com.itextpdf.text.pdf.PdfWriter; +import com.itextpdf.tool.xml.XMLWorkerFontProvider; +import com.itextpdf.tool.xml.XMLWorkerHelper; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.web.multipart.MultipartFile; + +import java.io.*; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class PdfUtils { + + + // 定义全局的字体静态变量 + private static Font titlefont; + private static Font headfont; + private static Font keyfont; + private static Font textfont; + // 最大宽度 + private static int maxWidth = 520; + + // 静态代码块 + static { + try { + // 不同字体(这里定义为同一种字体:包含不同字号、不同style) + BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); + titlefont = new Font(bfChinese, 16, Font.BOLD); + headfont = new Font(bfChinese, 14, Font.BOLD); + keyfont = new Font(bfChinese, 10, Font.BOLD); + textfont = new Font(bfChinese, 10, Font.NORMAL); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * html转换成pdf文件 + * + * @param htmlContent + * @throws Exception + */ + public MultipartFile html2Pdf(String htmlArticleTitle, String htmlContent) throws Exception { + htmlContent = this.content2Html(htmlContent); + + String path = System.getProperty("user.dir"); + + //根据日期建文件夹 + Date date = new Date(); + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + String format = formatter.format(date); + String[] split = format.split("-"); + File dir = new File(path + "/temporary"); + if (!dir.exists()) { + dir.mkdir(); + } + File dir2 = new File(path + "/temporary" + "/" + split[0]); + if (!dir2.exists()) { + dir2.mkdir(); + } + File dir3 = new File(path + "/temporary" + "/" + split[0] + "/" + split[1]); + if (!dir3.exists()) { + dir3.mkdir(); + } + File dir4 = new File(path + "/temporary" + "/" + split[0] + "/" + split[1] + "/" + split[2]); + if (!dir4.exists()) { + dir4.mkdir(); + } + File pdfFile = new File(dir4 + "/pdf" + "-" + System.currentTimeMillis() + ".pdf"); + //1 打开文件流 + Document document = new Document(); + FileOutputStream fos = new FileOutputStream(pdfFile); + System.out.println(pdfFile); + InputStream is = new ByteArrayInputStream(htmlContent.getBytes(StandardCharsets.UTF_8)); + //InputStream cssIs = new ByteArrayInputStream(getCssFile()); + PdfWriter writer = null; + try { + writer = PdfWriter.getInstance(document, fos); + //3. 设置字体 + XMLWorkerFontProvider fontProvider1 = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS); + //fontProvider1.register(path+"\\simsun.ttc");//本地 + fontProvider1.register(path + "/simsun.ttc");//部署服务器 + + //3 打开文档 + document.open(); + // 段落 + Paragraph paragraph = new Paragraph(htmlArticleTitle, titlefont); + paragraph.setAlignment(1); //设置文字居中 0靠左 1,居中 2,靠右 + paragraph.setIndentationLeft(12); //设置左缩进 + paragraph.setIndentationRight(12); //设置右缩进 + paragraph.setFirstLineIndent(24); //设置首行缩进 + paragraph.setLeading(20f); //行间距 + paragraph.setSpacingBefore(5f); //设置段落上空白 + paragraph.setSpacingAfter(10f); //设置段落下空白 + document.add(paragraph); + //4 html转为pdf + //XMLWorkerHelper.getInstance().parseXHtml(writer, document, is, cssIs, Charset.forName("UTF-8"), fontProvider1); + XMLWorkerHelper.getInstance().parseXHtml(writer, document, is, StandardCharsets.UTF_8, fontProvider1); + + } catch (DocumentException | IOException e) { + throw new RuntimeException("转pdf失败罗~"); + } finally { + if (null != writer) { + writer.flush(); + } + //5 关闭文档 + document.close(); + fos.close(); + //cssIs.close(); + is.close(); + writer.close(); + MultipartFile multipartFile = getMulipartFiles2(pdfFile + ""); + System.out.println("pdfFile = " + pdfFile); + return multipartFile; + } + } + + public MultipartFile getMulipartFiles2(String filePath) throws IOException { + File file = new File(filePath); + FileInputStream fileInputStream = new FileInputStream(file); + MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getName(), + "application/sql", fileInputStream); + long size = multipartFile.getSize(); + return multipartFile; + } + + + /** + * 获取html + * + * @return + */ + public String content2Html(String htmlContent) { + String COMPLETE_CONTENT = "" + htmlContent + ""; + String content = COMPLETE_CONTENT; + content = content.replace("
", "
"); + return content; + } + + /** + * 获取样式文件 + * + * @return + * @throws Exception + */ + protected byte[] getCssFile() throws Exception { + FileInputStream fileInputStream = new FileInputStream("D:\\editor.css"); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1204]; + int len = 0; + while ((len = fileInputStream.read(buffer)) != -1) { + outStream.write(buffer, 0, len); + } + fileInputStream.close(); + return outStream.toByteArray(); + } +} \ No newline at end of file