【React】- 跨域PDF预览、下载(改文件名)、打印

我们经常会碰到跨域来方位PDF,同时需要下载、打印的需求,通常由于浏览器的安全策略,可以预览,但是下载和打印可能会受限,这时候怎么办呢?

1.创建一个隐藏的标签

要下载 iframe 中的 PDF 文件,你可以通过以下步骤实现:

  1. 获取 PDF 文件的 URL :确保你已经有一个指向 PDF 文件的 URL,即 url。
  2. 创建一个下载链接:使用 JavaScript 创建一个隐藏的下载链接,并触发点击事件来下载文件。

以下是一个示例代码:

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 文件如下:

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方式

上面的问题通常是由于以下几个原因造成的:

  1. 浏览器行为:

    • 某些浏览器可能不会自动下载文件,而是会在新标签页中打开文件。这取决于浏览器的设置和文件类型。
  2. 文件 URL 的问题:

    • 确保 url 是一个有效的 URL,并且指向一个可以直接下载的文件资源。
  3. CORS 问题:

    • 如果 url 是一个跨域的 URL,浏览器可能会阻止下载操作,除非服务器正确设置了 CORS 头。
  4. 文件 MIME 类型:

    • 确保服务器返回的文件具有正确的 MIME 类型(例如,PDF 文件应为 application/pdf),以确保浏览器能够正确处理下载。

解决方法

1. 检查文件 URL

确保 url 是一个有效的 URL,并且指向一个可以直接下载的文件资源。你可以在浏览器中直接访问该 URL,看看是否能正常下载文件。

2. 检查 CORS 设置

如果 url 是一个跨域的 URL,确保服务器设置了正确的 CORS 头。例如,服务器应该返回以下头信息:

http 复制代码
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 的示例:

jsx 复制代码
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);
    }
};

示例代码解释

  1. 检查 URL:

    • 确保 url 存在且有效。
  2. 使用 fetch 获取文件:

    • 使用 fetch 发送请求以获取文件的二进制数据(blob)。
  3. 创建下载链接:

    • 创建一个临时的 URL 对象(URL.createObjectURL)。
    • 创建一个隐藏的 <a> 标签,并设置其 href 和 download 属性。
    • 触发点击事件以开始下载。
    • 移除临时的 <a> 标签并释放 URL 对象。

3.组件化

笔者直接提供一个react的PDF组件,可以直接复制到自己工程里使用。

工程结构

index.jsx代码

javascript 复制代码
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代码

javascript 复制代码
.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;
        }
    }
}

使用

javascript 复制代码
<RPdf src={url} download={yourDownloadName+'.pdf'} />
相关推荐
小天源3 小时前
智叟云集系统架构解析:从品牌 AI 可见度监测到前后端协同
人工智能·react.js·系统架构·node.js·react·ai监测·ai品牌监测
SensorFlow4 小时前
曝光埋点别放在组件渲染里:连续可见与去重范围怎么验收
javascript
harris14815435297584 小时前
能解码不等于能信任:JWT 的签名到底验的是什么
javascript
liqingdi4374 小时前
[SWPUCTF 2023 秋季新生赛]Easy SMC
服务器·前端·javascript
日耗10亿Token被老板赶出门4 小时前
REA 火了:一个 Electron 按钮的调用链,它能追到哪一步?
javascript·electron
青柠之夏cc5 小时前
Laravel服务容器底层原理揭秘
javascript·rpc·laravel
变与不变8066 小时前
防抖节流详解
开发语言·前端·javascript
用户6602676118796 小时前
HTTP 200,页面仍是旧状态:定位一次前后端字段错配
javascript
用户72526496629696 小时前
浏览器为什么加载不了超大图片
前端·javascript
donk13146 小时前
React Native for OpenHarmony 实战:三方库 react-native-step-indicator 的鸿蒙化适配指南
分布式·react native·react.js·wpf·harmonyos