JavaScript处理密码保护PDF的完整指南
PDF是最简单的文档共享格式之一。它们具有便携性,并能通过密码保护提供基本的访问控制。本文将讨论在JavaScript中解锁和打开密码保护PDF文档的多种方法之一。
本文使用PDF.js和现代浏览器内置的客户端JavaScript工具来实现:
- 从用户设备读取PDF文件
- 仅在PDF受密码保护时提示输入密码
- 显示解锁PDF文件失败尝试的反馈
- 使用浏览器的Canvas API渲染解密后的PDF页面
创建新的JavaScript项目
首先,让我们使用Vite设置一个原生JavaScript应用程序的脚手架。在终端中运行以下命令创建名为pdf-password的新项目并安装依赖:
1
|
npm create vite@latest -- --template vanilla pdf-password && cd pdf-password && npm install
|
接下来,安装PDF.js作为项目依赖:
创建处理用户输入和PDF渲染的HTML元素
将项目的index.html文件内容替换为以下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Locked PDF Viewer</title>
</head>
<body>
<form class="pdf-form">
<label for="pdf-form__input" class="pdf-form__label"
>View a PDF file in your browser</label
>
<input type="file" name="pdf-file" id="" class="pdf-form__input" />
</form>
<div class="password-form-backdrop">
<form class="password-form">
<label for="" class="password-form__label"
>The PDF is password-protected. Please enter its password.</label
>
<p class="password-incorrect">Incorrect password. Please try again.</p>
<input
type="password"
name="password"
class="password-input"
placeholder="PDF Password"
autocomplete=""
autofocus
/>
<button type="submit" class="password-submit">Unlock</button>
</form>
</div>
<canvas class="pdf-canvas"></canvas>
<script type="module" src="/src/main.js"></script>
</body>
</html>
|
第一个表单处理从用户设备上传文件,第二个表单收集PDF密码。canvas元素将用于渲染PDF页面。
导入项目依赖
删除项目src/main.js文件的内容,然后导入并配置PDF.js和项目的样式表:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import './style.css';
// Import pdfJs
import {
GlobalWorkerOptions,
getDocument,
PasswordResponses,
} from 'pdfjs-dist';
// Setup pdfJs' worker from the package's node_modules folder
GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/build/pdf.worker.mjs',
import.meta.url
).toString();
|
显示PDF内容
将以下代码复制到src/main.js文件中。它为pdf-form元素的文件输入字段附加事件监听器,检测用户何时选择PDF并尝试在屏幕上显示文档内容:
1
2
3
|
// Display an uploaded PDF file
document.querySelector('.pdf-form__input')
.addEventListener('change', viewPDF);
|
定义viewPDF事件处理程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
function viewPDF(e) {
// Get the File object of the uploaded PDF.
const file = e.target.files[0];
// Continue only if the user has uploaded a PDF document
if (file.type !== 'application/pdf') {
alert(`Error: ${file.name} is not a PDF file`);
return;
}
// Read the contents of the PDF file from the user's device
const reader = new FileReader();
reader.readAsArrayBuffer(file);
// Handle error(s) encountered while reading the contents of the PDF file
reader.onerror = () => {
alert(`Unable to read ${file.name} to an ArrayBuffer`);
console.error(reader.error);
};
// Wait till FileReader has read all contents of the PDF file before proceeding
reader.onload = async () => {
// Transform the contents of the PDF file to a generic byte array
const bytes = new Uint8Array(reader.result);
// Using PDF.js, start loading the PDF contents from the above byte array
const loadingTask = getDocument(bytes);
// Prompt for a password only if PDF.js detects password protection while loading the document
loadingTask.onPassword = handlePDFPassword;
// Complete the process of loading the PDF document
const pdfDocument = await loadingTask.promise;
// Hide the PDF upload form since we don't need it anymore
document.querySelector('.pdf-form').style.display = 'none';
renderPage(pdfDocument);
};
}
|
提示输入密码并解锁受密码保护的PDF
定义handlePDFPassword函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
function handlePDFPassword(setPassword, reason) {
const passwordForm = document.querySelector('.password-form-backdrop').style;
const passwordIncorrect = document.querySelector('.password-incorrect').style;
// Prompt for a password if PDF.js needs the file’s password to proceed
if (reason === PasswordResponses.NEED_PASSWORD) {
passwordForm.display = 'flex';
passwordIncorrect.display = 'none';
document.querySelector('.password-form').addEventListener('submit', (e) => {
e.preventDefault();
setPassword(document.querySelector('.password-input').value);
// Hide password prompt after the correct password is submitted
passwordForm.display = 'none';
passwordIncorrect.display = 'none';
});
}
// Display incorrect password error message if the entered password doesn’t unlock the PDF file
if (reason === PasswordResponses.INCORRECT_PASSWORD) {
passwordForm.display = 'flex';
passwordIncorrect.display = 'block';
}
}
|
渲染解锁的PDF页面
定义renderPage函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
async function renderPage(pdfDocument) {
// Load the first page of the document.
const page = await pdfDocument.getPage(1);
// Use the page's dimensions (in pixels) to set the
// dimensions of the canvas on which the page will be rendered
const viewport = page.getViewport({ scale: 1 });
const canvas = document.querySelector('.pdf-canvas');
const canvasContext = canvas.getContext('2d');
canvas.style.display = 'block';
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render the PDF page on the site
const renderTask = page.render({
canvasContext,
viewport,
});
await renderTask.promise;
}
|
进一步扩展
上面定义的renderPage函数仅渲染PDF文档的第一页。有关为应用程序添加分页和/或更好的错误处理的指导,请参阅https://mozilla.github.io/pdf.js/examples。