【React】- 跨域PDF预览、下载(改文件名)、打印
我们经常会碰到跨域来方位PDF,同时需要下载、打印的需求,通常由于浏览器的安全策略,可以预览,但是下载和打印可能会受限,这时候怎么办呢?
1.创建一个隐藏的标签
要下载 iframe
中的 PDF 文件,你可以通过以下步骤实现:
- 获取 PDF 文件的 URL:确保你已经有一个指向 PDF 文件的 URL,即
url
。 - 创建一个下载链接:使用 JavaScript 创建一个隐藏的下载链接,并触发点击事件来下载文件。
以下是一个示例代码:
// 假设 url是你已经定义的 PDF 文件 URL
const downloadPdf = () => {
const link = document.createElement('a');
link.href = url;
link.download = 'document.pdf'; // 你可以根据需要更改文件名
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
// 在你的组件中添加一个按钮来触发下载
<button onClick={downloadPdf}>下载 PDF</button>
将上述代码集成到你的 index.jsx
文件中,确保 slnPdfUrl
是有效的 PDF 文件 URL。用户点击按钮时,PDF 文件将被下载。
示例集成
假设你的 index.jsx
文件如下:
import React from 'react';
const YourComponent = ({ slnPdfUrl }) => {
const downloadPdf = () => {
const link = document.createElement('a');
link.href = slnPdfUrl;
link.download = 'document.pdf';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
return (
<div>
<iframe className='sln-detail-pdf'
src={slnPdfUrl}
width="100%" height="100%"
/>
<button onClick={downloadPdf}>下载 PDF</button>
</div>
);
};
export default YourComponent;
但是呢,有些浏览器会直接打开一个新窗口显示pdf,并没有下载PDF
2. Fetch方式
上面的问题通常是由于以下几个原因造成的:
-
浏览器行为:
- 某些浏览器可能不会自动下载文件,而是会在新标签页中打开文件。这取决于浏览器的设置和文件类型。
-
文件 URL 的问题:
- 确保
url
是一个有效的 URL,并且指向一个可以直接下载的文件资源。
- 确保
-
CORS 问题:
- 如果
url
是一个跨域的 URL,浏览器可能会阻止下载操作,除非服务器正确设置了 CORS 头。
- 如果
-
文件 MIME 类型:
- 确保服务器返回的文件具有正确的 MIME 类型(例如,PDF 文件应为
application/pdf
),以确保浏览器能够正确处理下载。
- 确保服务器返回的文件具有正确的 MIME 类型(例如,PDF 文件应为
解决方法
1. 检查文件 URL
确保 url
是一个有效的 URL,并且指向一个可以直接下载的文件资源。你可以在浏览器中直接访问该 URL,看看是否能正常下载文件。
2. 检查 CORS 设置
如果 url
是一个跨域的 URL,确保服务器设置了正确的 CORS 头。例如,服务器应该返回以下头信息:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type
3. 检查文件 MIME 类型
确保服务器返回的文件具有正确的 MIME 类型。例如,对于 PDF 文件,服务器应该返回 Content-Type: application/pdf
。
4. 使用 fetch
或 axios
下载文件
如果上述方法都不奏效,可以尝试使用 fetch
或 axios
来下载文件。以下是一个使用 fetch
的示例:
const slnDownload = async () => {
if (!url) {
message.error('PDF URL 无效');
return;
}
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('网络响应不正确');
}
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'document.pdf';
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
} catch (error) {
message.error('下载文件时出错: ' + error.message);
}
};
示例代码解释
-
检查 URL:
- 确保
url
存在且有效。
- 确保
-
使用
fetch
获取文件:- 使用
fetch
发送请求以获取文件的二进制数据(blob
)。
- 使用
-
创建下载链接:
- 创建一个临时的 URL 对象(
URL.createObjectURL
)。 - 创建一个隐藏的
<a>
标签,并设置其href
和download
属性。 - 触发点击事件以开始下载。
- 移除临时的
<a>
标签并释放 URL 对象。
- 创建一个临时的 URL 对象(
3.组件化
笔者直接提供一个react的PDF组件,可以直接复制到自己工程里使用。
工程结构
index.jsx代码
import React, { useRef, useEffect, useState } from 'react';
import { Button, message} from "antd";
import './index.less';
export default function RPdf(props, ref) {
const [url, setUrl] = useState('');
const iframeRef = useRef(null);
useEffect(() => {
init();
}, [props.src,props.download])
const init = async() => {
if (!props.src) {
return;
}
try {
const pdfResponse = await fetch(props.src);
if (!pdfResponse.ok) {
throw new Error('网络响应不正确');
}
const blob = await pdfResponse.blob();
const url = window.URL.createObjectURL(blob);
setUrl(url);
} catch (error) {
message.error('加载 PDF 时出错: ' + error.message);
}
}
const downloadPdf = async() => {
if (!url) {
message.error('PDF URL 无效');
return;
}
try {
const a = document.createElement('a');
a.href = url;
a.download = props.download;
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
} catch (error) {
message.error('下载文件时出错: ' + error.message);
}
}
const printPdf = () => {
if (!url) {
message.error('PDF URL 无效');
return;
}
const iframe = iframeRef.current;
if (!iframe) {
message.error('无法找到 iframe');
return;
}
iframe.contentWindow.focus();
iframe.contentWindow.print();
}
return (
<div className='r-pdf-container'>
<div className='r-pdf-header'>
<Button onClick={downloadPdf}>下载</Button>
<Button onClick={printPdf}>打印</Button>
</div>
<div className="r-pdf-content">
<iframe className='r-pdf'
ref={iframeRef}
src={url}
width="100%" height="100%"
/>
</div>
</div>
)
}
index.less代码
.r-pdf-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
gap: 6px;
margin: 12px;
.r-pdf-header {
height: 40px;
display: flex;
justify-content: flex-end;
gap: 8px;
}
.r-pdf-content {
width: 100%;
height: 100%;
// overflow: auto;
.r-pdf {
border: 0px;
}
}
}
使用
<RPdf src={url} download={yourDownloadName+'.pdf'} />