001/* 002 * Copyright (c) 2023-2026, Agents-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.agentsflex.core.file2text; 017 018 019import com.agentsflex.core.file2text.extractor.FileExtractor; 020import com.agentsflex.core.file2text.extractor.ExtractorRegistry; 021import com.agentsflex.core.file2text.source.*; 022 023import java.io.File; 024import java.io.InputStream; 025import java.util.List; 026import java.util.stream.Collectors; 027 028public class File2TextService { 029 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(File2TextService.class); 030 private final ExtractorRegistry registry; 031 032 public File2TextService() { 033 this(new ExtractorRegistry()); 034 } 035 036 public File2TextService(ExtractorRegistry registry) { 037 this.registry = registry; 038 } 039 040 public ExtractorRegistry getRegistry() { 041 return registry; 042 } 043 044 public String extractTextFromHttpUrl(String httpUrl) { 045 return extractTextFromSource(new HttpDocumentSource(httpUrl)); 046 } 047 048 public String extractTextFromHttpUrl(String httpUrl, String fileName) { 049 return extractTextFromSource(new HttpDocumentSource(httpUrl, fileName)); 050 } 051 052 public String extractTextFromHttpUrl(String httpUrl, String fileName, String mimeType) { 053 return extractTextFromSource(new HttpDocumentSource(httpUrl, fileName, mimeType)); 054 } 055 056 public String extractTextFromFile(File file) { 057 return extractTextFromSource(new FileDocumentSource(file)); 058 } 059 060 public String extractTextFromStream(InputStream is, String fileName, String mimeType) { 061 return extractTextFromSource(new ByteStreamDocumentSource(is, fileName, mimeType)); 062 } 063 064 public String extractTextFromBytes(byte[] bytes, String fileName, String mimeType) { 065 return extractTextFromSource(new ByteArrayDocumentSource(bytes, fileName, mimeType)); 066 } 067 068 069 /** 070 * 从 DocumentSource 提取纯文本 071 * 支持多 Extractor 降级重试 072 * 073 * @param source 文档输入源 074 * @return 提取的文本(非空去空格),若无法提取则抛出异常 075 * @throws IllegalArgumentException 输入源为空 076 */ 077 public String extractTextFromSource(DocumentSource source) { 078 if (source == null) { 079 throw new IllegalArgumentException("DocumentSource cannot be null"); 080 } 081 082 try { 083 // 获取可用的 Extractor(按优先级排序) 084 List<FileExtractor> candidates = registry.findExtractors(source); 085 if (candidates.isEmpty()) { 086 log.warn("No extractor supports this document: " + safeFileName(source)); 087 return null; 088 } 089 090 // 日志:输出候选 Extractor 091 log.info("Trying extractors for {}: {}", safeFileName(source), 092 candidates.stream() 093 .map(e -> e.getClass().getSimpleName()) 094 .collect(Collectors.joining(", "))); 095 096 097 for (FileExtractor extractor : candidates) { 098 try { 099 log.debug("Trying {} on {}", extractor.getClass().getSimpleName(), safeFileName(source)); 100 101 String text = extractor.extractText(source); 102 if (text != null && !text.trim().isEmpty()) { 103 log.debug("Success with {}: extracted {} chars", 104 extractor.getClass().getSimpleName(), text.length()); 105 return text; 106 } else { 107 log.debug("Extractor {} returned null", extractor.getClass().getSimpleName()); 108 } 109 } catch (Exception e) { 110 log.warn("Extractor {} failed on {}: {}", 111 extractor.getClass().getSimpleName(), 112 safeFileName(source), 113 e.toString()); 114 } 115 } 116 117 log.warn(String.format("All %d extractors failed for: %s", candidates.size(), safeFileName(source))); 118 return null; 119 } finally { 120 source.cleanup(); 121 } 122 123 } 124 125 private String safeFileName(DocumentSource source) { 126 try { 127 return source.getFileName() != null ? source.getFileName() : "unknown"; 128 } catch (Exception e) { 129 return "unknown"; 130 } 131 } 132}