SpringBoot WebFlux+Angular流式传输大页数PDF遇问题求助
大页数PDF流式传输问题:部分文件需全量下载才能显示
我尝试实现大体积PDF的流式传输,目前页数少于几千的文件能正常运行,但大页数PDF必须下载全部内容才能开始显示。经过测试:
- 226MB(304页)文件:仅需下载32MB即可开始展示,跳转页面时按需下载对应内容
- 453MB(608页)文件:需下载61MB开始展示
- 大页数文件:必须全量下载才能显示
后端服务代码(控制器仅调用此函数)
public HashMap<String, Long> getRanges(HttpHeaders headers, long size){ List<HttpRange> rangeHeader = headers.getRange(); Long start; Long end; Long contentLength; HashMap<String, Long> ranges = new HashMap<>(); ranges.put("size", size); if (!rangeHeader.isEmpty()) { start = Long.parseLong(String.valueOf(rangeHeader.get(0).getRangeStart(size))); ranges.put("start", start); end = size - 1; if (rangeHeader.get(0).getRangeEnd(size) > 0) { end = Long.parseLong(String.valueOf(rangeHeader.get(0).getRangeEnd(size))); } ranges.put("end", end); contentLength = end - start + 1; ranges.put("contentLength", contentLength); } else { ranges.put("start", 0L); ranges.put("end", size-1); ranges.put("contentLength", size); } return ranges; } public Mono<ResponseEntity<Resource>> getStreaming(String path, HttpHeaders headers) { return Mono.fromCallable(() -> { try { MinioClient minioClient = MinioClient.builder() .endpoint("http://127.0.0.1:9000") .credentials("admin", "password1") .build(); long size = minioClient.statObject( StatObjectArgs.builder() .bucket("test") .object(path) .build() ).size(); String typeFormat = minioClient.statObject( StatObjectArgs.builder() .bucket("test") .object(path) .build() ).contentType(); GetObjectArgs.Builder argsBuilder = GetObjectArgs .builder() .bucket("test") .object(path); HashMap<String, Long> ranges = getRanges(headers, size); InputStream obj = minioClient.getObject(argsBuilder .offset(ranges.get("start")) .length(ranges.get("contentLength")) .build()); InputStreamResource resource = new InputStreamResource(obj); HttpHeaders headers1 = new HttpHeaders(); headers1.set("Accept-Ranges", "bytes"); headers1.setContentLength(ranges.get("contentLength")); headers1.set("Content-Range", "bytes " + ranges.get("start") + "-" + ranges.get("end") + "/" + ranges.get("size")); if(!headers.getRange().isEmpty()){ return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT) .contentType(MediaType.valueOf(typeFormat)) .headers(headers1) .body(resource); }else { return ResponseEntity.ok() .contentType(MediaType.valueOf(typeFormat)) .headers(headers1) .body(resource); } } catch (Exception e) { throw new RuntimeException(e); } }); }
前端调用代码
<ngx-pdf-viewer [src]="'/service?path=pdf/test_262MB.pdf'"></ngx-pdf-viewer>
问题原因分析
PDF的流式加载能力完全依赖文件内部的结构设计:
- 线性结构PDF:这类PDF的页面内容按顺序存储,核心的交叉引用表(XRef Table) 放在文件末尾。阅读器必须读取到交叉引用表才能定位各个页面的字节位置,因此必须全量下载整个文件才能开始解析显示。大页数PDF如果是这种结构,就会出现你遇到的问题。
- Web优化格式PDF(Fast Web View):这类PDF将交叉引用表拆分并嵌入到文件的多个位置,同时把页面内容分块存储。阅读器可以先下载开头的交叉引用信息,快速定位到第一页的内容,实现流式加载。你测试的小页数大体积文件属于这种优化后的结构。
解决建议
- 检查PDF结构:用专业PDF工具(如Adobe Acrobat)打开大页数PDF,查看是否启用了"快速Web视图"(Fast Web View)。如果未启用,说明是线性结构。
- 转换为Web优化格式:使用工具将大页数PDF转换为优化格式,例如用Ghostscript执行以下命令:
gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dFastWebView=true -sOutputFile=optimized.pdf input.pdf - 后端代码确认:当前后端的Range请求处理逻辑基本正确,需确保MinIO的
getObject方法正确返回部分内容的流,且InputStreamResource没有将整个文件缓存到内存中(当前代码实现符合要求)。
内容的提问来源于stack exchange,提问作者PaoloBL
相关产品推荐
相关产品推荐

