使用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
相关产品推荐
相关产品推荐

