React中使用pdfjs预览pdf格式文件
实习中遇到一个需求,需要在前端预览pdf,word,和excel文件,因为pdf预览的资料比较多,先做了pdf预览的,使用的pdfjs-dist,踩坑及过程如下。
应用环境
vite@4.4.5
react@18.2.0
pdfjs-dist@2.12.313
踩坑1
用yarn add pdfjs-dist不下来包,必须指定版本
踩坑2
在引入psfjs-dist的时候,遇到很多奇奇怪怪的问题,先给出我下面可行的代码
import React from 'react';
import * as PdfJs from 'pdfjs-dist/legacy/build/pdf.js';
import { useState, useEffect, useCallback } from 'react';
import Axios from 'axios';
const pdfjsWorker = await import('pdfjs-dist/build/pdf.worker.entry');
// 设定pdfjs的 workerSrc 参数
// NOTE: 这一步要特别注意,网上很多关于pdfjs的使用教程里漏了这一步,会出现workerSrc未定义的错误
PdfJs.GlobalWorkerOptions.workerSrc = pdfjsWorker;
如果没有设置workerSrc参数,后面使用会报错404,很多人使用的webpack,因此他们用的require('pdfjs-dist/build/pdf.worker.entry'),但是我用的vite,是不支持require的,我尝试安装了vite-plugin-require-transform,但是它在react里似乎不起作用。后来查了vite的引入静态资源的方式,最终使用了import('pdfjs-dist/build/pdf.worker.entry')成功
下面就是具体实现代码啦。
- 这里是封装的一个选择文件的组件,因为我后面还要写docx,excel格式的文件。做的步骤就是,获取到input框选择的file文件,解析这个文件让其变成arrayBuffer格式,再用pdfjs去预览
import './index.css';
import { readBuffer, getExtend } from '@/components/fileView/utils.js';
import Pdf from '@/vendors/pdf/Pdf';
const Index = () => {
const [type, setType] = useState(null);
// const [file, setFile] = useState({});
const [loading, setLoading] = useState(false); //加载动画
const [arrayBuffer, setArrayBuffer] = useState({});
let buffer;
const handleChoosed = async (e) => {
setLoading(true);
try {
//解构赋值,e.target.files获取第一个元素
const [file] = e.target.files;
// console.log(test);
// console.log(e.target.files);
buffer = await readBuffer(file);
const { name } = file;
setType(getExtend(name));
setArrayBuffer(buffer);
console.log(arrayBuffer);
setLoading(false);
} catch (e) {
console.error(e);
} finally {
setLoading(false);
}
};
return (
<div className="pdf-wrap">
<input type="file" onChange={(e) => handleChoosed(e)}></input>
<div className="pdf-container">
{/* <canvas id="pdf-canvas"></canvas> */}
{type && <Pdf data={arrayBuffer} />}
{/* <Pdf data={arrayBuffer} /> */}
</div>
</div>
);
};
export const readBuffer = async (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (loadEvent) => resolve(loadEvent.target.result);
reader.onerror = (e) => reject(e);
reader.readAsArrayBuffer(file);
});
};
export function getExtend(name) {
const dot = name.lastIndexOf('.');
return name.substr(dot + 1);
}
- pdfjs渲染获取到的pdf文件。暂时只能单页渲染,点击上一页下一页时渲染新一页的内容
import React from 'react';
import * as PdfJs from 'pdfjs-dist/legacy/build/pdf.js';
import { useState, useEffect, useCallback } from 'react';
import Axios from 'axios';
const pdfjsWorker = await import('pdfjs-dist/build/pdf.worker.entry');
PdfJs.GlobalWorkerOptions.workerSrc = pdfjsWorker;
let pdfDoc;
const Pdf = ({ data }) => {
const [pageNum, setPageNum] = useState(1);
const [total, setTotal] = useState(1);
const [scale, setScale] = useState(1.5);
const loadFile = async () => {
//初始化pdf
console.log('初始化pdf');
pdfDoc = await PdfJs.getDocument(data).promise;
// console.log(pdfDoc.numPages);
setTotal(pdfDoc.numPages);
// console.log(pdfDoc);
};
const renderPdf = async (currentPageNum, currentScale) => {
console.log('渲染pdf页');
pdfDoc.getPage(currentPageNum).then((pageContent) => {
let canvas = document.getElementById(`canvas+${currentPageNum}`);
// let vp = pageContent.getViewport({ scale: currentScale });
// console.log(canvas);
let ctx = canvas.getContext('2d');
let dpr = window.devicePixelRatio || 1;
let bsr =
ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio ||
1;
let ratio = dpr / bsr;
let viewport = pageContent.getViewport({
scale: currentScale,
});
canvas.width = viewport.width * ratio;
canvas.height = viewport.height * ratio;
canvas.style.width = viewport.width + 'px';
canvas.style.height = viewport.height + 'px';
ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
let renderContext = {
canvasContext: ctx,
viewport: viewport,
};
pageContent.render(renderContext);
// setPageNum(currentPageNum + 1);
// setScale(currentScale);
});
};
const handleScaleAdd = () => {
//增大缩放倍数
setScale(scale + 0.1);
};
function handleScaleSub() {
//减小缩放倍数
if (scale > 0.5) {
//小于0.5不再进行缩小
setScale(scale - 0.1);
}
}
function prePage() {
if (pageNum > 1) {
setPageNum(pageNum - 1);
}
}
function nextPage() {
if (total > pageNum) {
setPageNum(pageNum + 1);
}
}
useEffect(() => {
loadFile();
}, [pageNum, scale, data]);
return (
<div>
<div className="pdf-container">
<div className="pdf-control-zoom">
<button onClick={prePage}>上一页</button>
<button onClick={nextPage}>下一页</button>
</div>
<canvas id={`canvas+${pageNum}`}></canvas>
</div>
<div className="pdf-control-zoom">
<button onClick={() => handleScaleAdd()} value="">
放大
</button>
<button onClick={() => handleScaleSub()} value="">
缩小
</button>
</div>
</div>
);
};
export default Pdf;
接下来会去实现按照滚动自动加载下一页,以及优化代码,菜鸟本鸟还是太菜了