如何检测 PDF 文件的是否已加载完成
通过 Fetch API 和 Blob 对象实现检测 PDF 是否已加载完成。
杭州
在网页中,如何判断 iframe 中的 PDF 是否已经加载完成呢?
网页中常规展示 PDF 的方式
<object data='filename.pdf' type='application/pdf'>
<p>您的浏览器不支持嵌入式 PDF</p>
</object>
上述写法虽然简洁,但存在一个问题 —— 你无法通过代码判断 PDF 是否加载完成。
尤其是对于较大的 PDF 文件(例如 10MB),加载过程可能需要几秒钟时间,用户体验不佳。
改进方案:使用 Fetch 请求 PDF 后再加载
为了更好地控制加载流程,我们可以使用 fetch 请求 PDF 文件,再通过 Blob 创建一个临时 URL 来展示 PDF。整体流程如下:
- 通过
fetch请求 PDF 文件; - 将请求到的文件转换为
Blob对象; - 使用
createObjectURL创建临时本地 URL; - 使用该 URL 来展示 PDF。
这样你可以在加载过程中设置 loading 状态,甚至配合进度条或提示信息,显著提升用户体验。
👉 示例代码 standbox:usePDFUrl
代码实现
import { useCallback, useEffect, useRef, useState } from 'react'
interface PdfRenderProps {
url: string // PDF 的远程地址
params: Record<string, any> // 可选的 PDF 参数,如页码等
}
// 将参数序列化为 URL 查询字符串
const serialization = (obj: Record<string, any>) => {
const params = new URLSearchParams()
for (let key in obj) {
params.append(key, obj[key])
}
return params.toString()
}
/**
* 自定义 Hook:根据远程 URL 异步加载 PDF 文件,并返回本地 URL
*/
export const usePdfUrl = (props: PdfRenderProps) => {
const { url, params } = props
const [pdfLocalUrl, setPdfLocalUrl] = useState<string | null>(null)
const [loading, setLoading] = useState(false)
const abortController = useRef<AbortController | null>(null)
// 拼接参数(如页码、缩放比例等)
const wrapperUrl = pdfLocalUrl ? `${pdfLocalUrl}#${serialization(params)}` : null
const fetchPdf = useCallback(async (requestUrl: string) => {
if (abortController.current) {
abortController.current.abort()
}
setLoading(true)
abortController.current = new AbortController()
const signal = abortController.current.signal
try {
const response = await fetch(requestUrl, { signal })
abortController.current = null
if (!response.ok) {
throw new Error(`HTTP 请求错误,状态码:${response.status}`)
}
const blob = await response.blob()
const pdfBlob = new Blob([blob], { type: 'application/pdf' })
const localUrl = URL.createObjectURL(pdfBlob)
setPdfLocalUrl(localUrl)
} catch (error) {
console.error('加载 PDF 失败:', error)
}
setLoading(false)
}, [])
useEffect(() => {
if (url) {
fetchPdf(url)
}
return () => {
abortController.current?.abort()
abortController.current = null
}
}, [fetchPdf, url])
return {
pdfLocalUrl: wrapperUrl,
loading,
}
}
总结
通过使用 fetch + Blob + URL.createObjectURL 的方式,我们可以更好地掌控 PDF 的加载过程:
- 可以监听加载状态(如 loading);
- 支持设置 URL 参数(如指定页码);
- 可扩展性更强,适用于需要精细控制的业务场景。
这种方法相比直接嵌入 <object> 更具灵活性。