package jnpf.dmsService.search.extract; import jnpf.dmsService.search.DmsSearchProperties; import lombok.RequiredArgsConstructor; import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.text.PDFTextStripper; import org.apache.poi.hwpf.HWPFDocument; import org.apache.poi.hwpf.extractor.WordExtractor; import org.apache.poi.xwpf.extractor.XWPFWordExtractor; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.springframework.stereotype.Service; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.Locale; @Service @RequiredArgsConstructor public class DmsTextExtractionService { private final DmsSearchProperties properties; public DmsTextExtractionResult extract(Path file, String fileName) throws IOException { if (Files.size(file) > properties.getMaxFileBytes()) { throw new IOException("文件超过全文检索解析大小限制"); } String extension = extension(fileName); String text; boolean ocrRequired = false; switch (extension) { case "doc": text = readDoc(file); break; case "docx": text = readDocx(file); break; case "pdf": PdfText pdf = readPdf(file); text = pdf.text; ocrRequired = pdf.ocrRequired; break; default: throw new IOException("全文检索仅支持 doc、docx、pdf 文件"); } return new DmsTextExtractionResult(limit(normalize(text)), ocrRequired); } private String readDoc(Path file) throws IOException { try (InputStream input = Files.newInputStream(file); HWPFDocument document = new HWPFDocument(input); WordExtractor extractor = new WordExtractor(document)) { return extractor.getText(); } } private String readDocx(Path file) throws IOException { try (InputStream input = Files.newInputStream(file); XWPFDocument document = new XWPFDocument(input); XWPFWordExtractor extractor = new XWPFWordExtractor(document)) { return extractor.getText(); } } private PdfText readPdf(Path file) throws IOException { StringBuilder text = new StringBuilder(); boolean ocrRequired = false; try (PDDocument document = Loader.loadPDF(file.toFile())) { PDFTextStripper stripper = new PDFTextStripper(); stripper.setSortByPosition(true); for (int page = 1; page <= document.getNumberOfPages(); page++) { stripper.setStartPage(page); stripper.setEndPage(page); String pageText = normalize(stripper.getText(document)); if (visibleCharacters(pageText) < properties.getMinPdfTextCharsPerPage()) { ocrRequired = true; } if (text.length() > 0) { text.append('\n'); } text.append(pageText); } } return new PdfText(text.toString(), ocrRequired); } private String extension(String fileName) { int separator = fileName == null ? -1 : fileName.lastIndexOf('.'); return separator < 0 ? "" : fileName.substring(separator + 1).toLowerCase(Locale.ROOT); } private String normalize(String value) { return value == null ? "" : value.replace("\u0000", "").trim(); } private int visibleCharacters(String value) { return value == null ? 0 : value.replaceAll("\\s+", "").length(); } private String limit(String value) { int max = Math.max(0, properties.getMaxTextChars()); return value.length() <= max ? value : value.substring(0, max); } private static final class PdfText { private final String text; private final boolean ocrRequired; private PdfText(String text, boolean ocrRequired) { this.text = text; this.ocrRequired = ocrRequired; } } }