PDF.js实现按需分片加载pdf文件-包含前后端开发源码和详细开发教程

PDF.js实现按需加载pdf文件

  • 说明
  • 前言
  • 前端项目
    • 分片加载的效果
    • 前端项目结构
    • 前端核心代码
    • 项目运行与访问
  • 后端项目
    • 项目结构
    • 核心代码实现
    • 注意事项
  • 项目源码
  • 关于没有实现按需加载的可能

说明

本文主要是介绍pdf.js的前后端项目的实现,包含可直接运行的源码。由于本人偏向于后端开发,因此前端的vue方面的demo介绍可能略有不足之处,敬请谅解。可运行源码放在文章末尾处,如果项目运行问题可私信

前言

本文主要是解决大体积pdf在线浏览加载缓慢,影响用户体验的问题。以及实现了分片加载后的,首次加载时自动加载了全部的pdf分片,导致浏览器报出内存不足的问题
技术栈为:SpringBoot、Vue、pdfjs
主要核心思路:前端请求时请求头附带请求范围range及读取大小,后端根据请求头返回相应的pdf文件流

前端项目

分片加载的效果

前端项目结构

PDF.js实现按需分片加载pdf文件-包含前后端开发源码和详细开发教程_第1张图片
PDF.js实现按需分片加载pdf文件-包含前后端开发源码和详细开发教程_第2张图片

前端核心代码

Home index.vue
预览文件按钮触发跳转到Pdf文件夹index.vue页面

<template>
  <div class="container">
    <el-button type="primary" @click="toLoad">预览文件el-button>
  div>
template>

<script>
export default {
  data() {
    return {};
  },
  mounted() {},
  methods: {
    toLoad() {
      this.$router.push("/pdf");
    },
  },
};
script>

<style lang="scss" scoped>
.container {
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
style>

Pdf index.vue

获取后端返回pdf文件流的接口,this.baseUrl为本地的后端项目接口:http://localhost:8181
this.src = ${this.baseUrl}/v1/pdf/load;

<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;
      // 数据文件流 转成 pdf
      this.src = `${this.baseUrl}/v1/pdf/load`;
    },
    // 禁用鼠标右击、F12 来禁止打印和打开调试工具
    prohibit() {
      document.oncontextmenu = function (ev) {
        return false; //屏蔽右键菜单
      };
      document.onkeydown = function (e) {
        if (
          e.ctrlKey &&
          (e.keyCode === 65 ||
            e.keyCode === 67 ||
            e.keyCode === 73 ||
            e.keyCode === 74 ||
            e.keyCode === 80 ||
            e.keyCode === 83 ||
            e.keyCode === 85 ||
            e.keyCode === 86 ||
            e.keyCode === 117)
        ) {
          return false;
        }
        if (e.keyCode === 18 || e.keyCode === 123) {
          return false;
        }
      };
    },
  },
  mounted() {
    this.$nextTick(() => {
      this.getPdfCode();
    });
  },
};
script>

<style lang="scss" scoped>style>

项目运行与访问

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

npm install

执行完成后,输入运行命令

npm run serve

PDF.js实现按需分片加载pdf文件-包含前后端开发源码和详细开发教程_第3张图片

后端项目

项目结构

后端项目是我在学习别的项目时创建,因此在上面临时写了一个分片加载的接口,直接运行即可
PDF.js实现按需分片加载pdf文件-包含前后端开发源码和详细开发教程_第4张图片

核心代码实现

pdf分片加载的后端实现类 PDFController.java

 package com.zhouquan.controller;

import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;

/**
 * @author zhouquan
 * @description todo
 * @date 2022-07-29 10:29
 **/
@RestController
@RequestMapping("/v1/pdf")
public class PDFController {

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

        File pdf = ResourceUtils.getFile("classpath:泛函分析教程(第2版).pdf");
        try (
                InputStream is = new FileInputStream(pdf);
                BufferedInputStream bis = new BufferedInputStream(is);
                OutputStream os = response.getOutputStream();
                BufferedOutputStream bos = new BufferedOutputStream(os)) {

            // 下载的字节范围
            int startByte, endByte, totalByte;
            if (request != null && request.getHeader("range") != null) {
                // 断点续传
                String[] range = request.getHeader("range").replaceAll("[^0-9\\-]", "").split("-");
                // 文件总大小
                totalByte = is.available();
                // 下载起始位置
                startByte = Integer.parseInt(range[0]);
                // 下载结束位置
                if (range.length > 1) {
                    endByte = Integer.parseInt(range[1]);
                } else {
                    endByte = totalByte - 1;
                }
                // 返回http状态
                response.setStatus(206);
            } else {
                // 正常下载
                // 文件总大小
                totalByte = is.available();
                // 下载起始位置
                startByte = 0;
                // 下载结束位置
                endByte = totalByte - 1;
                // 返回http状态
                response.setHeader("Accept-Ranges", "bytes");
                response.setStatus(200);
            }
            // 需要下载字节数
            int length = endByte - startByte + 1;

            //表明服务器支持分片加载
            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(length);

            //需要设置此属性,否则浏览器默认不会读取到响应头中的Accept-Ranges属性,因此会认为服务器端不支持分片,所以会直接全文下载
            response.setHeader("Access-Control-Expose-Headers", "Accept-Ranges,Content-Range");

            // 响应内容
            bis.skip(startByte);
            int len = 0;
            byte[] buff = new byte[1024 * 64];
            while ((len = bis.read(buff, 0, buff.length)) != -1) {
                if (length <= len) {
                    bos.write(buff, 0, length);
                    break;
                } else {
                    length -= len;
                    bos.write(buff, 0, len);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

跨域配置类 CORSFilter.java

package com.zhouquan.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 {
    }
}

注意事项

1.首次加载返回状态码200,注意以下属性服务器端在响应头中务必要加上
PDF.js实现按需分片加载pdf文件-包含前后端开发源码和详细开发教程_第5张图片

 //表明服务器支持分片加载
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(length);

//需要设置此属性,否则浏览器默认不会读取到响应头中的Accept-Ranges属性,因此会认为服务器端不支持分片,所以会直接全文下载
response.setHeader("Access-Control-Expose-Headers", "Accept-Ranges,Content-Range");

2.之后每次请求都会返回206,即已经实现分片加载。Content-Range: bytes 0-65535/408244,表明此次返回的文件范围
PDF.js实现按需分片加载pdf文件-包含前后端开发源码和详细开发教程_第6张图片
PDF.js实现按需分片加载pdf文件-包含前后端开发源码和详细开发教程_第7张图片

项目源码

前端:
前端基于Vue实现pdf.js对pdf的分片加载和按需加载功能

后端: 可私信找我要
Springboot实现pdf的分片加载功能

或者

https://zhouquanquan.lanzouw.com/b04e69lsd
密码:cj41

关于没有实现按需加载的可能

描述
pdf.js实现了分片加载,但是在首次加载时自动加载了全部的pdf分片,导致打开了几个超过1g的pdf时,浏览器报出内存不足的提示

解决方案
实现pdf.js的按需加载,只需要修改属性disableAutoFetch为true,表示关闭自动获取,只会请求需要的分片,从而实现了按需加载,disableAutoFetch的默认值是false,表示会自动获取所有分片

你可能感兴趣的:(#,SpringBoot,vue,javascript,pdf,前端,springboot,vue.js)