You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PDFBox合并S3中PDF文件时遇End-of-file异常排查

问题描述

我在AWS S3上存储了多个PDF文件,想要合并成一个PDF。通过InputStream数组读取S3文件后用PDF合并工具处理,循环添加所有InputStream后执行合并的代码可以正常运行:

for (PS3ObjectStream pFileS3Obj: PS3ObjectStream ) {
    try {
        pdfMerger.addSource(pFileS3Obj.getS3ObjectInputStream());
        
    }catch(Exception e) {
        e.printStackTrace();
    }
}
pdfMerger.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());

但循环内每次添加InputStream后就执行合并的代码报错:End-of-file , expected line,异常栈如下:

java.io.IOException: Error: End-of-File, expected line
at org.apache.pdfbox.pdfparser.BaseParser.readLine(BaseParser.java:1107)
at org.apache.pdfbox.pdfparser.COSParser.parseHeader(COSParser.java:2650)
at org.apache.pdfbox.pdfparser.COSParser.parsePDFHeader(COSParser.java:2633)
at org.apache.pdfbox.pdfparser.PDFParser.parse(PDFParser.java:219)
at org.apache.pdfbox.pdmodel.PDDocument.load(PDDocument.java:1230)
at org.apache.pdfbox.pdmodel.PDDocument.load(PDDocument.java:1148)
at org.apache.pdfbox.multipdf.PDFMergerUtility.legacyMergeDocuments(PDFMergerUtility.java:455)
at org.apache.pdfbox.multipdf.PDFMergerUtility.mergeDocuments(PDFMergerUtility.java:346)
at com.djcs.pslintegration.pdfutility.mergeService.MergePdfFileService.mergePDfFiles(MergePdfFileService.java:92)
at com.djcs.pslintegration.pdfutility.mergeService.MergePdfFileService.combinePdfFiles(MergePdfFileService.java:47)
at com.djcs.pslintegration.pdfutility.controller.PDFUtilityController.combinePdfFiles(PDFUtilityController.java:98)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:681)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743)
排查与解决方向
  • 理解PDFMergerUtility工作机制:PDFMergerUtility调用mergeDocuments后,会消费所有已添加的InputStream,且内部会清空源列表。第二次循环时,再次添加新流后调用合并,工具可能处于异常状态,尝试读取已消费的流导致EOF错误。
  • 检查InputStream可复用性:S3的S3ObjectInputStream是一次性的,被读取后无法再次读取。第一次合并已将流读完,后续操作复用该流会读到文件末尾。
  • 调整分批合并逻辑:若需每次添加一个文件就合并到目标PDF,需每次创建新的PDFMergerUtility实例,或把每次合并的结果作为下一次的源。示例代码:
    PDDocument mergedDoc = null;
    for (PS3ObjectStream pFileS3Obj : PS3ObjectStream) {
        try (InputStream is = pFileS3Obj.getS3ObjectInputStream()) {
            PDDocument currentDoc = PDDocument.load(is);
            if (mergedDoc == null) {
                mergedDoc = currentDoc;
            } else {
                PDFMergerUtility merger = new PDFMergerUtility();
                merger.addSource(mergedDoc);
                merger.addSource(currentDoc);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                merger.setDestinationStream(baos);
                mergedDoc.close(); // 关闭旧的合并文档
                mergedDoc = merger.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());
                currentDoc.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 最后处理合并后的mergedDoc,比如写入S3或本地文件
    
  • 验证S3文件完整性:异常栈显示解析PDF头失败,不排除个别S3文件损坏。可单独读取每个文件的流,尝试解析为PDDocument,确认是否有报错。
  • 检查PDFBox版本:不同版本的PDFMergerUtility内部实现有差异,可能是版本bug导致状态异常,尝试升级到最新稳定版。

内容的提问来源于stack exchange,提问作者John D

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 15:24:21