文件上传这块,很多人第一反应还是先按后缀名分支:pdf走一套,docx走一套,xlsx再抄一套。代码写到第三个格式的时候,自己都开始嫌弃。真到了线上,用户传个老版doc、ppt、odt,再来一份带附件的邮件文件,解析链路马上就乱。
这种事我一般不先写一堆if else,先找一个能把“识别格式”和“抽取内容”一把兜住的东西。Apache Tika 干的就是这个活。放进 SpringBoot 里,接口层收文件,服务层丢给 Tika,先拿正文,再按需要补元数据,基本就够用了。写法上也不重。整体思路和参考文里那种“直接落问题、少讲空话”的气质是一致的。
先把依赖加上:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parsers-standard-package</artifactId>
<version>2.9.2</version>
</dependency>
这里我一般直接上standard-package,别一开始就省那点包体积。你要解析 Word、Excel、PDF、PPT,最后大概率还是会补回来。
核心服务不用写复杂,先把最小闭环跑通:
@Service
publicclass DocumentParseService {
public ParsedDoc parse(MultipartFile file) {
if (file == null || file.isEmpty()) {
thrownew IllegalArgumentException("文件不能为空");
}
try (InputStream inputStream = file.getInputStream()) {
AutoDetectParser parser = new AutoDetectParser();
BodyContentHandler handler = new BodyContentHandler(-1);
Metadata metadata = new Metadata();
ParseContext context = new ParseContext();
parser.parse(inputStream, handler, metadata, context);
ParsedDoc result = new ParsedDoc();
result.setFileName(file.getOriginalFilename());
result.setContentType(metadata.get(Metadata.CONTENT_TYPE));
result.setContent(handler.toString().trim());
result.setTitle(metadata.get(TikaCoreProperties.TITLE));
result.setAuthor(metadata.get(TikaCoreProperties.CREATOR));
return result;
} catch (Exception e) {
thrownew RuntimeException("文档解析失败: " + file.getOriginalFilename(), e);
}
}
}
BodyContentHandler(-1)这个地方别偷懒。很多人默认不设,文档一大,正文直接被截断,前面看着正常,后面悄悄没了。这个坑跟线上超时、默认配置那类问题一个味道:不是不能用,是默认值未必适合你。
接口层就很薄,别把解析逻辑塞控制器里:
@RestController
@RequestMapping("/doc")
publicclass DocumentController {
privatefinal DocumentParseService documentParseService;
public DocumentController(DocumentParseService documentParseService) {
this.documentParseService = documentParseService;
}
@PostMapping("/parse")
public ParsedDoc parse(@RequestParam("file") MultipartFile file) {
return documentParseService.parse(file);
}
}
返回对象简单点,够用就行:
public class ParsedDoc {
private String fileName;
private String contentType;
private String title;
private String author;
private String content;
// getter/setter
}
跑起来之后,常见格式像pdf、docx、xlsx、pptx,Tika 基本都能认。它的好处不只是“能解析”,而是你不需要自己维护一堆格式判断。比如用户传了个扩展名写错的文件,单靠后缀你容易误判,Tika 会先探测 MIME 类型,再决定用哪个解析器,这一步能省掉不少脏活。
再往前走一步,实际项目里我一般会补两个东西。
第一个是大小限制,不然用户甩个 200MB 扫描版 PDF 过来,接口线程卡着不动,GC 也开始抖:
spring:
servlet:
multipart:
max-file-size: 20MB
max-request-size: 20MB
第二个是异步化。解析小文件同步搞没问题,批量导入、知识库入库这种场景,最好扔线程池或者 MQ,别让上传接口一直等。尤其是 OCR、图片型 PDF,那已经不是“轻松解析”了,是另一类活。
最后说句实在的。SpringBoot + Tika 这套东西,最值钱的不是代码有多花,而是你终于不用围着文件后缀打转。上传、识别、抽取正文、拿元数据,这条链路先统一起来,后面做检索、摘要、敏感词扫描、向量化入库,路就顺了。
有些技术看上去只是省几百行代码,真到项目里,它省的是你后面一串分支判断和排障时间。这个才值钱。