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

这篇具有很好参考价值的文章主要介绍了SpringBoot+PDF.js实现按需分片加载预览(包含可运行示例源码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

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

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

分片加载的效果

springboot本地引入pdf.worker.min.js,# SpringBoot,vue,spring boot,分片加载,vue,pdf.js,按需加载

前端项目

前端项目结构

springboot本地引入pdf.worker.min.js,# SpringBoot,vue,spring boot,分片加载,vue,pdf.js,按需加载

springboot本地引入pdf.worker.min.js,# SpringBoot,vue,spring boot,分片加载,vue,pdf.js,按需加载

前端核心代码

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>

springboot本地引入pdf.worker.min.js,# SpringBoot,vue,spring boot,分片加载,vue,pdf.js,按需加载

前端项目运行

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

npm install
npm run serve

springboot本地引入pdf.worker.min.js,# SpringBoot,vue,spring boot,分片加载,vue,pdf.js,按需加载

后端项目

后端项目结构

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

springboot本地引入pdf.worker.min.js,# SpringBoot,vue,spring boot,分片加载,vue,pdf.js,按需加载

后端核心代码

这段代码实现了使用 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/7285041
            totalByte = is.available();
            startByte = 0;
            endByte = totalByte - 1;
            response.setStatus(HttpServletResponse.SC_OK);
             // 写入一些数据到输出流中,否则火狐浏览器会报错:ns_error_net_partinal_transfer
            bos.write(1);
        } 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 {

    @Override
    public 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);
    }

    @Override
    public void destroy() {
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
}

后端项目运行

springboot本地引入pdf.worker.min.js,# SpringBoot,vue,spring boot,分片加载,vue,pdf.js,按需加载

项目运行效果

springboot本地引入pdf.worker.min.js,# SpringBoot,vue,spring boot,分片加载,vue,pdf.js,按需加载

首次访问

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

springboot本地引入pdf.worker.min.js,# SpringBoot,vue,spring boot,分片加载,vue,pdf.js,按需加载

 // 表明服务器支持分片加载
 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,返回响应信息如下:

springboot本地引入pdf.worker.min.js,# SpringBoot,vue,spring boot,分片加载,vue,pdf.js,按需加载

springboot本地引入pdf.worker.min.js,# SpringBoot,vue,spring boot,分片加载,vue,pdf.js,按需加载

项目源码

springboot本地引入pdf.worker.min.js,# SpringBoot,vue,spring boot,分片加载,vue,pdf.js,按需加载

链接: https://pan.baidu.com/s/1KNn2HE_ZudMRyzPK8_wIjA?pwd=zhou
提取码: zhou文章来源地址https://www.toymoban.com/news/detail-842793.html

到了这里,关于SpringBoot+PDF.js实现按需分片加载预览(包含可运行示例源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 微信小程序实现PDF预览功能——pdf.js(含源码解析)

    前言 前一段时间遇到了一个需求,关于 pdf 文件的预览,客户要求如下: 只能在微信小程序内预览,不能调起本地浏览器预览; 需要让用户强制阅读 10s 后才算阅读完成,进而进行下一步操作; 用户不能下载预览的 pdf 文件; 因为一些原因(此处省略一万字🐎),这个项目

    2023年04月09日
    浏览(37)
  • vue2 使用pdf.js 实现pdf预览,并可复制文本

            需求:pdf预览,并且可以选中pdf的内容进行复制。                 在ruoyi的vue前端项目中用到,参考了网上不少文章,因为大部分没给具体的pdf.js版本,导致运行过程中报各种api 错误,经过尝试以下版本可用,故记录一下:         安装依赖:         vue 页面

    2024年01月19日
    浏览(54)
  • springboot+微信小程序实现文件上传下载(预览)pdf文件

    实现思路: 选择文件 wx.chooseMessageFile ,官方文档: https://developers.weixin.qq.com/miniprogram/d e v/api/media/image/wx.chooseMessageFile.html 上传文件 `wx,uploadFile` , 官方文档:https://developers.weixin.qq.com/miniprogram/dev/api/network/upload/wx.uploadFile.html 查看所有上传的pdf文件,显示在页面上 点击pdf文件

    2024年02月08日
    浏览(69)
  • Android之WebView加载PDF链接预览PDF文件

    Android的webview压根就不支持加载pdf,Android与iOS不同,iOS加载pdf,不管本地还是在线,直接使用webview渲染就可以了,而Android却做不到,所以我们必须得扣脑壳了。方法也有很多种,比如第三方PDFview,MuPDF等,但是不推荐,引入进去apk体积会大很多,所以大多场景都是通过js解析

    2024年02月13日
    浏览(48)
  • SpringBoot+vue文件上传&下载&预览&大文件分片上传&文件上传进度

    SpringBoot+vue 大文件分片下载 Blob File spark-md5根据文件内容生成hash 大文件分片上传(批量并发,手动上传)vue组件封装-form组件 vue上传大文件/视频前后端(java)代码 springboot+vue自定义上传图片及视频 SpringBoot + VUE实现前台上传文件获取实时进度( 使用commons-fileupload设置上传监听

    2024年02月05日
    浏览(74)
  • 【PDF.js】PDF文件预览

    使用PDFJS实现pdf文件的预览,支持预览指定页、搜索、缩略图、页面尺寸调整等等。 官方地址 文档地址 下载地址 将下载的压缩包解压并放入到项目中的public文件夹下,我这里下载的是pdfjs-4.0.379-dist版本,如下 在 pdfjs-4.0.379-dist/web/viewer.mjs 内搜索 throw new Error(“file or

    2024年04月11日
    浏览(60)
  • pdf.js预览pdf文件

    预览pdf一般通过浏览器自带的pdf预览器就可以,但有时候需要窗口预览或自定义操作,可以使用pdf.js操作 pdf.js需要构建后使用,我们可以直接下载安装pdfjs-dist,这是构建好的版本 这里注意你的环境,新版本使用了可选链,空值合并和私有 class 字段/方法等,如果你的浏览器

    2024年02月03日
    浏览(65)
  • 利用PDF.js在微信小程序里预览PDF文件

    在微信小程序可以通过wx.downloadFile 和 wx.openDocument 两个api下载并打开pdf文件。这种方式主要有不少的缺点: 1、需要下载才可以查看,且每次打开都需要下载生成一个临时文件,如果PDF文件比较多的话,临时文件会越来越多,且如果PDF文件比较大的话,打开会比较慢。 2、在导

    2024年02月03日
    浏览(72)
  • js下载图片、pdf等文件,无预览

    直接使用window.open()或window.locat.href()下载文件遇到图片或pdf文件就会跳转预览页,不能满足我想要的点击直接下载文件到本地的需求,尝试多次,最终通过以下方法实现了我的需求。 鉴于后端返回的是文件路径,首先要将文件url地址转为文件对象,代码如下: npm install saveA

    2024年02月13日
    浏览(44)
  • Webpack 怎么实现按需异步加载模块

    要弄懂这个问题,需要先来看关于webpack打包的3个问题。 第一个问题 项目中的json文件,如何使用webpack进行处理? 如果我们希望把json文件当做静态配置,例如有如下json文件 在其他模块中引用: 要实现上面的效果,应该如何配置? 如果我们希望把json文件当做静态资源加载,

    2024年01月17日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包