2

经常做pdf预览的小伙伴都知道,如 pdfjs,pdfjs-dist,react-pdf等等,各种库,但是其实就是为了预览一下pdf内容。
而且pdfjs-dist还会依赖node-pre-gyp,做前端的都知道,这个gpy是有多恶心。

好了,为了不用任何库,就可以预览pdf,你可以如下操作:

1. 新窗口打开或将pdf地址放在iframe 的src里预览

写法如下:

<iframe src="https://example.com/test.pdf"></iframe>
这么做有一个问题,如果pdf的地址请求头是application/octet-stream的话,pdf会被下载下来,无法预览。

2. 使用ajax获取pdf之后,在iframe里预览。

import { useEffect, useState } from 'react';

const App = () => {
    const [errorMsg, setErrorMsg] = useState('')
    const [pdfBlobUrl, setPdfBlobUrl] = useState('')
    const [loading, setLoading] = useState(false)
    const [progress, setProgress] = useState(0)

    const fetchPdfWithXHR = () => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', 'https://example.com/test.pdf', true); // 替换为实际的接口地址

        // 设置请求头(根据需要)
        xhr.setRequestHeader('Content-Type', 'application/json');

        // 指定返回类型为 Blob
        xhr.responseType = 'blob';

        // 请求成功处理
        xhr.onload = () => {
            // 由于带进度下载的请求,status是206,不是200,所这里做一下处理。
            if (xhr.status>=200 && xhr.status<300) {
                // 将 Blob 数据生成 URL
                const blob = new Blob([xhr.response], { type: 'application/pdf' });
                const url = URL.createObjectURL(blob);
                setPdfBlobUrl(url);
            } else {
                setErrorMsg('Failed to fetch PDF:' + xhr.status + xhr.statusText);
            }
        };

        // 下载进度监听器
        xhr.onprogress = (event) => {
            if (event.lengthComputable) {
                const v = loaded / total * 100
                setProgress(Math.floor(v))
            }
        }

        // 错误处理
        xhr.onerror = () => {
            console.error('Network error while fetching PDF.');
        };

        // 发送请求
        const requestBody = JSON.stringify({
            // 替换为实际的请求参数
            token: '',
        });
        xhr.send(requestBody);
    };

    useEffect(() => {
        fetchPdfWithXHR();
    }, []);

    return loading ? <div>Loading PDF {progress}%...</div> : pdfBlobUrl ? (
        <iframe
            src={pdfBlobUrl}
            width="100%"
            height="750px"
            style={{ border: 'none' }}
        />
    ) : (
        <p>{errorMsg}</p>
    );
};

export default App;

这样做,无论是什么样的pdf,都可以实现预览了。当然,如果有跨域问题的请求,是不能用ajax的

如果既是跨域,又是application/octet-stream,这种情况,直接跳转新页面下载吧。用其他任何库,都无法解决的。

另外附上对应的java代码:

import org.springframework.core.io.FileSystemResource;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.io.RandomAccessFile;
import java.io.OutputStream;

@RestController
public class FileDownloadController {

    @GetMapping("/download")
    public ResponseEntity<Void> downloadFile(
            @RequestParam String filePath,
            @RequestHeader(value = "Range", required = false) String rangeHeader,
            HttpServletResponse response) throws Exception {

        File file = new File(filePath);
        if (!file.exists()) {
            return ResponseEntity.notFound().build();
        }

        long fileLength = file.length();
        long start = 0;
        long end = fileLength - 1;

        if (rangeHeader != null && rangeHeader.startsWith("bytes=")) {
            String[] ranges = rangeHeader.substring(6).split("-");
            start = Long.parseLong(ranges[0]);
            if (ranges.length > 1 && !ranges[1].isEmpty()) {
                end = Long.parseLong(ranges[1]);
            }
        }

        long contentLength = end - start + 1;

        response.setContentType("application/pdf");
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getName());
        response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(contentLength));
        response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
        response.setStatus(HttpStatus.PARTIAL_CONTENT.value());

        try (RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
             OutputStream out = response.getOutputStream()) {

            byte[] buffer = new byte[8192];
            randomAccessFile.seek(start);
            long remaining = contentLength;

            while (remaining > 0) {
                int bytesRead = randomAccessFile.read(buffer, 0, (int) Math.min(buffer.length, remaining));
                if (bytesRead == -1) {
                    break;
                }
                out.write(buffer, 0, bytesRead);
                remaining -= bytesRead;
            }
        }

        return ResponseEntity.ok().build();
    }
}

jsoncode
4k 声望786 粉丝

I'm jsoncode