SpringBoot+PDF.js实现按需分片加载预览(包含可运行示例源码)

本文介绍了如何使用SpringBoot和Vue.js结合PDF.js技术,实现在线预览大体积PDF文件的分片加载策略,通过处理Range请求头实现断点续传,有效提高加载速度和降低内存消耗,同时展示了前后端核心代码实现和跨域配置。
摘要由CSDN通过智能技术生成

SpringBoot+PDF.js实现按需分片加载预览

  • 前言
  • 分片加载的效果
  • 前端项目
    • 前端项目结构
    • 前端核心代码
    • 前端项目运行
  • 后端项目
    • 后端项目结构
    • 后端核心代码
    • 后端项目运行
  • 项目运行效果
    • 首次访问
    • 分片加载
  • 项目源码

前言

本文的解决方案旨在解决大体积PDF在线浏览加载缓慢、影响用户体验的难题。通过利用分片加载技术,前端请求时附带range及读取大小信息,后端据此返回相应的PDF文件流。这种方式有效地减轻了服务器和浏览器的负担,提升了加载速度和用户体验。同时解决了首次加载全部分片导致浏览器内存不足的问题。

技术栈:Spring Boot、Vue和pdf.js。

分片加载的效果

在这里插入图片描述

前端项目

前端项目结构

在这里插入图片描述

image-20240223172511041

前端核心代码

index.vue

<template>
  <div class="pdf">
    <iframe
      :src="`/static/pdf/web/viewer.html?file=${encodeURIComponent(src)}`"
      frameborder="0"
      style="width: 100%; height: calc(100vh)"
    ></iframe>
  </div>
</template>

<script>
import baseUrl from "@/api/baseurl.js";
export default {
  data() {
    return {
      baseUrl: baseUrl.baseUrl,
      src: "",
      loading: false,
    };
  },
  created() {},
  methods: {
    getPdfCode: function () {
      this.loading = true;
      // 这里是请求分片的接口,看情况修改
      this.src = `http://localhost:8181/v1/pdf/load`;
    },
  },
  mounted() {
    this.$nextTick(() => {
      this.getPdfCode();
    });
  },
};
</script>

<style lang="scss" scoped></style>

image-20240224132317559

前端项目运行

首先确保vue需要的运行环境已经安装(nodejs),我使用的版本:12.18.2,然后使用vscode打开项目,在终端输入命令:

npm install
npm run serve

image-20240223173450002

后端项目

后端项目结构

本示例只是一个简单的springboot项目,核心文件PDFController.java用于分片加载接口,CORSFilter.java为跨域配置

image-20240224132650175

后端核心代码

这段代码实现了使用 PDF.js 进行分片加载 PDF 文件的功能。下面是代码的主要实现思路:

  1. 首先,通过 ResourceUtils.getFile 方法获取类路径下的 PDF 文件,并将其读取为字节数组 pdfData
  2. 然后,判断文件大小是否小于指定的阈值(1MB),如果小于阈值,则直接将整个文件作为响应返回。修改了小体积pdf小于分片大小时无法访问的bug
  3. 如果文件大小超过阈值,就根据请求头中的 Range 字段判断是否为断点续传请求。
  4. 如果是首次请求或者没有 Range 字段,则返回整个文件的字节范围,并设置响应状态为 SC_OK(响应码200)。
  5. 如果是断点续传请求,则解析 Range 字段获取请求的起始位置和结束位置,并根据这些位置从文件中读取相应的字节进行响应。
  6. 在响应头中设置 Accept-RangesContent-Range 属性,告知客户端服务器支持分片加载,并指定本次返回的文件范围。
  7. 最后,设置响应的内容类型为 application/octet-stream,内容长度为本次返回的字节数,然后刷新输出流,将数据返回给客户端。

这样,客户端就可以使用 PDF.js 来分片加载显示 PDF 文件了。

PDFController.java

/**
/**
 * pdf分片加载的后端实现
 *
 * @param response
 * @param request
 * @throws FileNotFoundException
 */
@GetMapping("/load")
public void loadPDFByPage(HttpServletResponse response, HttpServletRequest request) throws FileNotFoundException {
   

    // 获取pdf文件,建议pdf大小超过20mb以上
    File pdf = ResourceUtils.getFile("classpath:需要分片加载的pdf.pdf");
    byte[] pdfData = new byte[0];
    try {
   
        pdfData = FileUtils.readFileToByteArray(pdf);
    } catch (IOException e) {
   
        throw new RuntimeException(e);
    }

    // 以下为pdf分片的代码
    try (InputStream is = new ByteArrayInputStream(pdfData)
  • 24
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 21
    评论
这个问题涉及到前后端的代码实现要分别给出前端和后端的代码示例。 前端实现: 1. 安装 pdf.js 库:可以使用 npm 安装,命令为 `npm install pdfjs-dist --save` 2. 在 Vue 组件中引入 pdf.js 库: ``` import pdfjsLib from 'pdfjs-dist' ``` 3. 在组件中定义 PDF 的方法,代码如下: ``` previewPdf() { // 从服务器获取 PDF 文件的 URL const url = 'http://localhost:8080/api/pdf/preview?fileId=1234567890' // 创建一个 canvas 用于绘制 PDF const canvas = this.$refs.previewCanvas // 获取 PDF 渲染上下文 const context = canvas.getContext('2d') // PDF 文件 pdfjsLib.getDocument(url).promise.then(pdf => { // 获取 PDF 第一页 pdf.getPage(1).then(page => { // 设置 canvas 尺寸与页面大小相同 const viewport = page.getViewport({scale: 1}) canvas.width = viewport.width canvas.height = viewport.height // 渲染 PDF 到 canvas 上 page.render({ canvasContext: context, viewport: viewport }) }) }) } ``` 4. 在模板中添 canvas 和按钮,代码如下: ``` <template> <div> <canvas ref="previewCanvas"></canvas> <button @click="previewPdf"> PDF</button> </div> </template> ``` 后端实现: 1. 使用 Spring Boot 框架实现一个 RESTful API,用于获取 PDF 文件的 URL。代码示例如下: ``` @RestController @RequestMapping("/api/pdf") public class PdfController { @Autowired private SftpService sftpService; @GetMapping("/preview") public ResponseEntity<Resource> previewPdf(@RequestParam("fileId") String fileId) { // 从 SFTP 服务器上下文件到本地临时目录 File tempFile = sftpService.download(fileId); // 将文件转换为 Spring Resource 对象 Path path = tempFile.toPath(); ByteArrayResource resource = null; try { resource = new ByteArrayResource(Files.readAllBytes(path)); } catch (IOException e) { e.printStackTrace(); } // 返回文件内容和响应头 HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=" + tempFile.getName()); headers.add(HttpHeaders.CONTENT_TYPE, "application/pdf"); headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(tempFile.length())); return ResponseEntity.ok() .headers(headers) .body(resource); } } ``` 2. 在 Spring Boot 应用程序中配置 SFTP 服务器连接信息和文件保存路径,代码示例如下: ``` @Configuration public class SftpConfig { @Value("${sftp.host}") private String host; @Value("${sftp.port}") private int port; @Value("${sftp.username}") private String username; @Value("${sftp.password}") private String password; @Value("${sftp.remoteDirectory}") private String remoteDirectory; @Value("${sftp.localDirectory}") private String localDirectory; @Bean public SftpService sftpService() { SftpConfig config = new SftpConfig(); config.setHost(host); config.setPort(port); config.setUsername(username); config.setPassword(password); config.setRemoteDirectory(remoteDirectory); config.setLocalDirectory(localDirectory); return new SftpServiceImpl(config); } } ``` 3. 实现 SftpService 接口,用于下 PDF 文件到本地临时目录。代码示例如下: ``` public interface SftpService { File download(String fileId); } @Service public class SftpServiceImpl implements SftpService { private final SftpConfig config; public SftpServiceImpl(SftpConfig config) { this.config = config; } @Override public File download(String fileId) { Session session = null; ChannelSftp channel = null; try { JSch jsch = new JSch(); session = jsch.getSession(config.getUsername(), config.getHost(), config.getPort()); session.setPassword(config.getPassword()); session.setConfig("StrictHostKeyChecking", "no"); session.connect(); channel = (ChannelSftp) session.openChannel("sftp"); channel.connect(); channel.cd(config.getRemoteDirectory()); // 生成本地临时文件名 String localFileName = UUID.randomUUID().toString() + ".pdf"; File localFile = new File(config.getLocalDirectory() + File.separator + localFileName); // 下文件到本地临时目录 channel.get(fileId, localFile.getAbsolutePath()); return localFile; } catch (JSchException | SftpException e) { e.printStackTrace(); } finally { if (channel != null) { channel.disconnect(); } if (session != null) { session.disconnect(); } } return null; } } ``` 要注意的是,SFTP 服务器的连接信息要在配置文件中进行配置,例如: ``` sftp.host=192.168.1.100 sftp.port=22 sftp.username=admin sftp.password=123456 sftp.remoteDirectory=/pdf sftp.localDirectory=/tmp ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徐州蔡徐坤

又要到饭了兄弟们

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值