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

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);BufferedInputStream bis = new BufferedInputStream(is);OutputStream os = response.getOutputStream();BufferedOutputStream bos = new BufferedOutputStream(os)) {// 下载的字节范围int startByte, endByte, totalByte;// 获取文件总大小int fileSize = pdfData.length;int minSize = 1024 * 1024;// 如果文件小于1 MB,直接返回数据,不需要进行分片if (fileSize < minSize) {// 直接返回整个文件response.setStatus(HttpServletResponse.SC_OK);response.setContentType("application/octet-stream");response.setContentLength(fileSize);bos.write(pdfData);return;}// 根据HTTP请求头的Range字段判断是否为断点续传if (request == null || request.getHeader("range") == null) {// 如果是首次请求,返回全部字节范围 bytes 0-7285040/7285041totalByte = is.available();startByte = 0;endByte = totalByte - 1;response.setStatus(HttpServletResponse.SC_OK);} else {// 断点续传逻辑String[] range = request.getHeader("range").replaceAll("[^0-9\\-]", "").split("-");// 文件总大小totalByte = is.available();// 下载起始位置startByte = Integer.parseInt(range[0]);// 下载结束位置endByte = range.length > 1 ? Integer.parseInt(range[1]) : totalByte - 1;// 跳过输入流中指定的起始位置bis.skip(startByte);// 表示服务器成功处理了部分 GET 请求,返回了客户端请求的部分数据。response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);int bytesRead, length = endByte - startByte + 1;byte[] buffer = new byte[1024 * 64];while ((bytesRead = bis.read(buffer, 0, Math.min(buffer.length, length))) != -1 && length > 0) {bos.write(buffer, 0, bytesRead);length -= bytesRead;}}// 表明服务器支持分片加载response.setHeader("Accept-Ranges", "bytes");// Content-Range: bytes 0-65535/408244,表明此次返回的文件范围response.setHeader("Content-Range", "bytes " + startByte + "-" + endByte + "/" + totalByte);// 告知浏览器这是一个字节流,浏览器处理字节流的默认方式就是下载response.setContentType("application/octet-stream");// 表明该文件的所有字节大小response.setContentLength(endByte - startByte + 1);// 需要设置此属性,否则浏览器默认不会读取到响应头中的Accept-Ranges属性,// 因此会认为服务器端不支持分片,所以会直接全文下载response.setHeader("Access-Control-Expose-Headers", "Accept-Ranges,Content-Range");// 第一次请求直接刷新输出流,返回响应response.flushBuffer();} catch (IOException e) {e.printStackTrace();}
}

CORSFilter.java 通用的跨域配置

package com.example.pdfload.filter;import org.springframework.stereotype.Component;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Component
public class CORSFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletResponse response1 = (HttpServletResponse) response;response1.addHeader("Access-Control-Allow-Credentials", "true");response1.addHeader("Access-Control-Allow-Origin", "*");response1.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");response1.addHeader("Access-Control-Allow-Headers","range,Accept-Ranges,Content-Range,Content-Type," +"X-CAF-Authorization-Token,sessionToken,X-TOKEN,Cache-Control,If-Modified-Since");if (((HttpServletRequest) request).getMethod().equals("OPTIONS")) {response.getWriter().println("ok");return;}chain.doFilter(request, response);}@Overridepublic void destroy() {}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}
}

后端项目运行

image-20240224133652301

项目运行效果

image-20240224134625354

首次访问

首次访问返回状态码200,返回响应信息如下:

image-20240224135153313

 // 表明服务器支持分片加载response.setHeader("Accept-Ranges", "bytes");// Content-Range: bytes 0-65535/408244,表明此次返回的文件范围response.setHeader("Content-Range", "bytes " + startByte + "-" + endByte + "/" + totalByte);// 告知浏览器这是一个字节流,浏览器处理字节流的默认方式就是下载response.setContentType("application/octet-stream");// 表明该文件的所有字节大小response.setContentLength(endByte - startByte + 1);// 需要设置此属性,否则浏览器默认不会读取到响应头中的Accept-Ranges属性,// 因此会认为服务器端不支持分片,所以会直接全文下载response.setHeader("Access-Control-Expose-Headers", "Accept-Ranges,Content-Range");

分片加载

分片加载返回状态码206,返回响应信息如下:

image-20240224135805018

image-20240224140040627

项目源码

image-20240224141259487

链接:https://pan.baidu.com/s/1oD9bUvGfFmfEimXfaKGpXg?pwd=zhou
提取码:zhou

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/707593.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C 程序结构

在我们学习 C 语言的基本构建块之前&#xff0c;让我们先来看看一个最小的 C 程序结构&#xff0c;在接下来的章节中可以以此作为参考。 C Hello World 实例 C 程序主要包括以下部分&#xff1a; 预处理器指令函数变量语句 & 表达式注释 让我们看一段简单的代码&#x…

Fabric V2.5 通用溯源系统——区块链部分设计

本节对Fabric V2.5 通用溯源系统的区块链部分做一个简单的介绍,包括目录结构、文件作用、设计思路。此节内容免费发布在TrueTechLabs Fabric学习交流QQ群。 购买专栏前请认真阅读:《Fabric项目学习笔记》专栏介绍 TrueTechLabs Fabric学习交流QQ群: 一、区块链部分文件目录简…

基于Python校园鲜花水果商城系统(Django框架)开题答辩常规问题和如何回答(答辩指导)

博主介绍&#xff1a;黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者&#xff0c;CSDN博客专家&#xff0c;在线教育专家&#xff0c;CSDN钻石讲师&#xff1b;专注大学生毕业设计教育和辅导。 所有项目都配有从入门到精通的基础知识视频课程&#xff…

React最常用的几个hook

React最常用的几个Hook包括&#xff1a;useState、useEffect、useRef以及useContext。 useState&#xff1a; 用于在函数组件中添加状态管理。它返回一个数组&#xff0c;第一个元素是当前状态的值&#xff0c;第二个元素是更新状态的函数。在使用时&#xff0c;可以通过解构赋…

代码库管理工具Git介绍

阅读本文同时请参阅-----免费的Git图形界面工具sourceTree介绍 Git是一个分布式版本控制系统&#xff0c;它可以帮助开发者跟踪和管理代码历史。Git的命令行工具是使用Git的核心方式&#xff0c;虽然它可能看起来有些复杂&#xff0c;但是一旦掌握了基本命令&#xff0c;你…

【C++私房菜】序列式容器的迭代器失效问题

目录 一、list的迭代器失效 二、vector的迭代器失效 1、空间缩小操作 2、空间扩大操作 三、总结 在C中&#xff0c;当对容器进行插入或删除操作时&#xff0c;可能会导致迭代器失效的问题。所谓迭代器失效指的是&#xff0c;原先指向容器中某个元素的迭代器&#xff0c;在…

【每日前端面经】2023-02-28

题目来源: 牛客 使用TS的目的 提供很好的智能提示方便进行代码重构明确定义参数类型和函数重载 Type和Interface的区别 type主要用于创建联合类型、交叉类型、以及定义复杂的类型别名interface主要用于定义对象和类的结构 Any和泛型 虽然用any类型能够接收任何类型的参数…

Retrofit核心原理

Retrofit是一个类型安全的HTTP客户端库&#xff0c;广泛用于Android和Java应用中&#xff0c;用于简化网络请求和响应的处理。本文将深入探讨Retrofit的核心原理&#xff0c;帮助开发者理解其背后的工作机制。 Retrofit简介 Retrofit是Square公司开发的一个开源库&#xff0c…

MWC 2024丨美格智能推出5G RedCap系列FWA解决方案,开启5G轻量化新天地

2月27日&#xff0c;在MWC 2024世界移动通信大会上&#xff0c;美格智能正式推出5G RedCap系列FWA解决方案。此系列解决方案具有低功耗、低成本等优势&#xff0c;可以显著降低5G应用复杂度&#xff0c;快速实现5G网络接入&#xff0c;提升FWA部署的经济效益。 RedCap技术带来了…

YOLO V5、SAM、RESNET50模型在GPU环境下搭建过程

好的&#xff0c;我将提供更详细的步骤来搭建YOLOv5、SAM和ResNet50模型在GPU环境下的过程。 **1. 环境设置&#xff1a;** 确保你的环境满足以下要求&#xff1a; - CUDA和CuDNN已正确安装&#xff0c;并与你的GPU兼容。 镜像下载cudnntensorflow_cudnn镜像下载-CSDN博客 …

pclpy Ransac平面分割算法输出的索引从点云中提取点云的子集

pclpy Ransac平面分割算法输出的索引从点云中提取点云的子集 一、算法原理二、代码三、结果1.sor统计滤波2.Ransac内点分割平面3.Ransac外点分割平面 四、相关数据 一、算法原理 1、Ransac介绍 RANSAC(RAndom SAmple Consensus,随机采样一致)算法是从一组含有“外点”(outlier…

Flink CDC 提取记录变更时间作为事件时间和 Hudi 表的 precombine.field 以及1970-01-01 取值问题

博主历时三年精心创作的《大数据平台架构与原型实现&#xff1a;数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行&#xff0c;点击《重磅推荐&#xff1a;建大数据平台太难了&#xff01;给我发个工程原型吧&#xff01;》了解图书详情&#xff0c;…

如何使用ArcGIS Pro为栅格图添加坐标信息

在某些时候&#xff0c;我们从网上获取的资源是一张普通的栅格图&#xff0c;没有任何的坐标信息&#xff0c;如果想要和带坐标信息的数据一起使用就需要先添加坐标信息&#xff0c;在GIS上&#xff0c;我们把这个过程叫做地理配准&#xff0c;这里为大家介绍一下地理配准的方法…

golang的反射探索

1、golang中反射常用的场景 1》类型检查—通用类包或者函数的时候&#xff0c;在运行时可以动态的获取任意对象的类型信息 2》动态调用方法—运行时动态的选择使用哪个方法 3》结构体标签处理—结构体字段一般是通过tag来注解。运行时可以通过反射读取tag。常用于解析配置文件&…

雾锁王国Enshrouded服务器CPU内存配置怎么选择?

雾锁王国/Enshrouded服务器CPU内存配置如何选择&#xff1f;阿里云服务器网aliyunfuwuqi.com建议选择8核32G配置&#xff0c;支持4人玩家畅玩&#xff0c;自带10M公网带宽&#xff0c;1个月90元&#xff0c;3个月271元&#xff0c;幻兽帕鲁服务器申请页面 https://t.aliyun.com…

使用 Go 语言读取文件内容并进行反序列化

在现代软件开发过程中&#xff0c;经常需要读取配置文件或数据文件&#xff0c;并将这些文件的内容转换成程序可以理解和操作的数据结构。对于使用Go语言的开发者来说&#xff0c;标准库中提供的一系列工具和包能够帮助完成从文件读取到数据反序列化的整个流程&#xff0c;特别…

通过shell编写内存监视的脚本来介绍一些基本shell脚本操作

目录 知识概览 总体脚本编写 date awk grep bc 知识概览 总体脚本编写 #!/bin/bash#定义日志的文件名和日期 cdate$(date %Y%m%d%H%M%S) logfile"/tmp/memlog_{$0}.log"#拿到ip ip_addr$(ip add|grep "ens33$"|awk {print $2})#总内存和使用的内存 m…

成为大佬之路--linux软件安装使用第000000018篇--linux安装nacos

官网 Nacos官网 | Nacos 官方社区 | Nacos 下载 | Nacos 安装包 Releases alibaba/nacos GitHub 安装 1.创建目录 mkdir -p /opt/nacos cd /opt/nacos 2.下载安装包 wget https://github.com/alibaba/nacos/releases/download/1.4.7/nacos-server-1.4.7.tar.gz 3.解压…

如何使用Fastapi上传文件?先从请求体数据讲起

文章目录 1、请求体数据2、form表单数据3、小文件上传1.单文件上传2.多文件上传 4、大文件上传1.单文件上传2.多文件上传 1、请求体数据 前面我们讲到&#xff0c;get请求中&#xff0c;我们将请求数据放在url中&#xff0c;其实是非常不安全的&#xff0c;我们更愿意将请求数…

springboot/ssm高校疫情防控系统Java校园疫情防控管理平台web

springboot/ssm高校疫情防控系统Java校园疫情防控管理平台web 基于springboot(可改ssm)vue项目 开发语言&#xff1a;Java 框架&#xff1a;springboot/可改ssm vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff1a;…