React blob格式在线预览(支持图片、视频、pdf、doc、excel)

写了个弹窗,传请求参数,在里面请求后端数据,数据流为blob格式,通过各种解析渲染,展示相应数据预览。

相关组件安装

npm install @js-preview/excel --save // 预览excel
npm install docx-preview --save  // 预览doc

创建组件

在components文件下创建PreviewFile文件再里面创建index.tsx和index.css(主要是对excel文件样式zu)

index.css样式文件

.freviewFile .vue-office-excel{
    min-height: 700px;
    width: 100%;
    min-width: 700px;
    overflow-x: hidden;
}
.freviewFile  .x-spreadsheet-toolbar{
    width: 100%;
    overflow-x: auto;
}

 index.tsx相关

1. 引入

import { ModalForm } from "@ant-design/pro-components";
import React, { useEffect, useState } from "react";
import { getToken } from "@/utils";
import { downloadFile } from "@/services/ant-design-pro/project";
import Axios from "axios";
import { message, Spin } from 'antd';
import jsPreviewExcel from '@js-preview/excel';
import '@js-preview/excel/lib/index.css';
import './index.css';

2. 组件传参

code是向后端的传参,关闭弹窗引发的函数

code: string;
onClose(): void;

3. 组件内部参数

 const fileTypeMap = {
        "xls": "application/vnd.ms-excel",
        "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        "doc": "application/msword",
        "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        "pdf": "application/pdf",
        "ppt": "application/pdf",
        "pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
        "png": "image/png",
        "gif": "image/gif",
        "jpeg": "image/jpeg",
        "jpg": "image/jpeg",
        "txt": "text/plain",
    }
    const imgType = ['bmp', 'jpg', 'jpeg', 'png', 'tif', 'gif', 'pcx', 'tga', 'exif', 'fpx', 'svg', 'psd', 'cdr', 'pcd', 'dxf', 'ufo', 'eps', 'ai', 'raw', 'WMF', 'webp', 'avif', 'apng'];
    const videoType = ['wmv', 'asf', 'asx', 'rm', 'rmvb', 'mp4', '3gp', 'mov', 'm4v', 'avi', 'dat', 'mkv', 'flv', 'vob'];
    const token = getToken(); // 用于axios请求,header中传入token
    const [loading, setLoading] = useState(false); // 加载效果
    const [modalVisit, setModalVisit] = useState(false); // 控制弹窗显隐
    const [fileName, setFileName] = useState(''); //文件名称
    const [blobUrl, setBlobUrl] = React.useState(''); //文件流地址

4. 获取文件名函数

  const getFileName = (code: string) => {
        const lastSlashIndex = code.lastIndexOf('/');
        const fileName = code.substring(lastSlashIndex + 1);
        return fileName;
    }

5. 获取文件类型

 const getFileType = (name: string) => {
        if (name) {
            if (name.lastIndexOf(".") > -1) {
                return name.slice(name.lastIndexOf(".") + 1);
            } else {
                return false
            }
        }
    }

6. 重置组件数据函数

  const onClear = () => {
        blobUrl && window.URL.revokeObjectURL(blobUrl);
        setBlobUrl('');
        setFileName('');
        const freviewFileDom = document.getElementById('freviewFile')
        while (freviewFileDom?.firstChild) {
            freviewFileDom.removeChild(freviewFileDom.firstChild);
        }
    }

7.请求后端数据,解析数据,再创建相应DOM渲染在页面(!!!!)

 const getFile = (code: string) => {
        setLoading(true);
        const fileName = getFileName(code);
        setFileName(fileName);
        const fileType = getFileType(code);
        if (!fileType) {
            return
        }

        Axios({
            url: downloadFile, // 后端请求接口
            method: "GET",
            responseType: 'blob',
            headers: {
                "Content-Type": " application/json;charset=UTF-8",
                "cattoken": token
            },
            params: { code: code }
        }).then(res => {
            const blob = new Blob([res.data])
            let reader = new FileReader();
            if (fileType === 'xls' || fileType === 'xlsx') {
                reader.readAsArrayBuffer(blob);
                reader.onload = async (e: any) => {
                    const box = document.createElement('div');
                    box.style.width = '94%';
                    box.style.minHeight = '700px';
                    box.style.marginLeft = '3%';
                    const myExcelPreviewer = jsPreviewExcel.init(box)
                    myExcelPreviewer
                        .preview(e.target.result)
                        .then(() => {
                            console.info('预览完成')
                            setLoading(false)
                        })
                        .catch((e) => {
                            console.info('预览失败', e)
                            setLoading(false)
                        })
                    const freviewFileDom = document.getElementById('freviewFile');
                    while (freviewFileDom?.firstChild) {
                        freviewFileDom.removeChild(freviewFileDom.firstChild);
                    }
                    freviewFileDom?.appendChild(box);
                }
            } else {
                reader.readAsText(blob);
                reader.onload = async (e: any) => {
                    if (e.target.result.indexOf('"statusCode"') >= 0) {
                        message.error(JSON.parse(e.target.result).message)
                    } else {
                        if (imgType.includes(fileType)) {
                            // 图片类预览
                            const blobUrl = URL.createObjectURL(blob);
                            var img = document.createElement('img');
                            img.onload = function () {
                                // 释放创建的URL对象
                                window.URL.revokeObjectURL(blobUrl);
                                setLoading(false);
                            };
                            img.src = blobUrl;
                            img.style.width = 'auto';
                            img.style.height = '300px';
                            const freviewFileDom = document.getElementById('freviewFile')
                            while (freviewFileDom?.firstChild) {
                                freviewFileDom.removeChild(freviewFileDom.firstChild);
                            }
                            freviewFileDom?.appendChild(img);
                        } else if (videoType.includes(fileType)) {
                            const blobUrl = URL.createObjectURL(blob);
                            // console.log('触发', videoType.includes(fileType), fileType, blobUrl)
                            // 创建video元素
                            const video = document.createElement('video');
                            video.controls = true;
                            video.style.width = '100%'; // 设置视频宽度为容器宽度
                            video.style.maxHeight = '100%'; // 设置视频最大高度为容器高度
                            // 当视频加载完成后释放URL对象
                            video.onended = function () {
                                URL.revokeObjectURL(blobUrl);
                            };
                            // 设置视频源
                            video.src = blobUrl;
                            setLoading(false)
                            // 获取预览容器元素
                            const freviewFileDom = document.getElementById('freviewFile')
                            while (freviewFileDom?.firstChild) {
                                freviewFileDom.removeChild(freviewFileDom.firstChild);
                            }
                            freviewFileDom?.appendChild(video);

                            // 将video元素添加到预览容器中
                            freviewFileDom?.appendChild(video);
                        } else if (fileType === 'pdf' || fileType === 'txt') {
                            const blob = new Blob([res.data], { type: fileTypeMap[fileType] })
                            const blobUrl = URL.createObjectURL(blob);
                            const iframe = document.createElement('iframe');
                            iframe.src = blobUrl;
                            setBlobUrl(() => blobUrl);
                            iframe.style.width = '100%';
                            iframe.style.minHeight = '700px';
                            iframe.onload = function () {
                                setLoading(false);
                            };
                            const freviewFileDom = document.getElementById('freviewFile');
                            while (freviewFileDom?.firstChild) {
                                freviewFileDom.removeChild(freviewFileDom.firstChild);
                            }
                            freviewFileDom?.appendChild(iframe);

                        } else if (fileType === 'doc' || fileType === 'docx') {
                            const blob = new Blob([res.data], { type: fileTypeMap[fileType] });
                            const box = document.createElement('div');
                            box.style.width = '80%';
                            box.style.minHeight = '700px';
                            box.style.marginLeft = '10%';
                            const freviewFileDom = document.getElementById('freviewFile');
                            while (freviewFileDom?.firstChild) {
                                freviewFileDom.removeChild(freviewFileDom.firstChild);
                            }
                            freviewFileDom?.appendChild(box);
                            let docx = require("docx-preview")
                            docx.renderAsync(blob, box).then(() => {
                                setLoading(false);
                                console.log("docx: finished")
                            }).catch(() => {
                                setLoading(false);
                                box.innerHTML = '文件解析失败,可下载预览';
                            })
                        } else {
                            setLoading(false);
                            const box = document.createElement('div');
                            box.style.width = '100%';
                            box.style.height = '300px';
                            box.style.textAlign = 'center';
                            box.style.fontSize = '18px';
                            box.style.lineHeight = '300px';
                            box.innerHTML = `暂不支持预览${fileType}格式,请自行下载预览`;
                            const freviewFileDom = document.getElementById('freviewFile');
                            while (freviewFileDom?.firstChild) {
                                freviewFileDom.removeChild(freviewFileDom.firstChild);
                            }
                            freviewFileDom?.appendChild(box);
                        }
                    }
                }
            }
        }).catch(err => {
            console.log(err);
            setLoading(false);
            const box = document.createElement('div');
            box.style.width = '100%';
            box.style.height = '300px';
            box.style.textAlign = 'center';
            box.style.fontSize = '18px';
            box.style.lineHeight = '300px';
            box.innerHTML = '解析文件失败了,可下载文件查看';
            const freviewFileDom = document.getElementById('freviewFile');
            while (freviewFileDom?.firstChild) {
                freviewFileDom.removeChild(freviewFileDom.firstChild);
            }
            freviewFileDom?.appendChild(box);
        })
    }

8. 监听code,控制弹窗

 useEffect(() => {
        if (code) {
            setModalVisit(true);
            if (getFileType(code)) {
                getFile(code);
            }
        } else {
            onClear();
        }

        return () => {
            onClear()
        }
    }, [code]);


// 监听弹窗,触发关闭函数
    useEffect(() => {
        if (!modalVisit) {
            onClose();
        }
    }, [modalVisit])

9.jsx

return (
        <ModalForm
            width="70vw"
            title="文件预览"
            open={modalVisit}
            style={{
                minHeight: '50vh',
                maxHeight: '90vh',
                overflowY: 'auto'
            }}
            onOpenChange={setModalVisit}
            submitter={false}
        >
            <Spin spinning={loading} delay={500} tip="加载中">
                <h1 style={{ fontSize: 18, marginBottom: 10 }}>{fileName}</h1>
                <div id="freviewFile" className="freviewFile" style={{ minHeight: 500 }}></div>
            </Spin>
        </ModalForm>
    )

index.tsx组件完整代码

1import { ModalForm } from "@ant-design/pro-components";
import React, { useEffect, useState } from "react";
import { getToken } from "@/utils";
import { downloadFile } from "@/services/ant-design-pro/project";
import Axios from "axios";
import { message, Spin } from 'antd';
import jsPreviewExcel from '@js-preview/excel';
import '@js-preview/excel/lib/index.css';
import './index.css';


type PreviewFileProps = {
    code: string;
    onClose(): void;
}
const PreviewFile: React.FC<PreviewFileProps> = ({ code, onClose }) => {
    const fileTypeMap = {
        "xls": "application/vnd.ms-excel",
        "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        "doc": "application/msword",
        "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        "pdf": "application/pdf",
        "ppt": "application/pdf",
        "pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
        "png": "image/png",
        "gif": "image/gif",
        "jpeg": "image/jpeg",
        "jpg": "image/jpeg",
        "txt": "text/plain",
    }
    const imgType = ['bmp', 'jpg', 'jpeg', 'png', 'tif', 'gif', 'pcx', 'tga', 'exif', 'fpx', 'svg', 'psd', 'cdr', 'pcd', 'dxf', 'ufo', 'eps', 'ai', 'raw', 'WMF', 'webp', 'avif', 'apng'];
    const videoType = ['wmv', 'asf', 'asx', 'rm', 'rmvb', 'mp4', '3gp', 'mov', 'm4v', 'avi', 'dat', 'mkv', 'flv', 'vob'];
    // const wordType = ['text', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'rar', 'zip', '7z', 'apz', 'ar', 'bz', 'car', 'dar', 'cpgz', 'f', 'ha', 'hbc', 'hbc2', 'hbe', 'hpk', 'hyp'];
    const token = getToken();
    const [loading, setLoading] = useState(false);
    const [modalVisit, setModalVisit] = useState(false);
    const [fileName, setFileName] = useState('');
    const [blobUrl, setBlobUrl] = React.useState('');
    const getFileName = (code: string) => {
        const lastSlashIndex = code.lastIndexOf('/');
        const fileName = code.substring(lastSlashIndex + 1);
        return fileName;
    }
    const getFileType = (name: string) => {
        if (name) {
            if (name.lastIndexOf(".") > -1) {
                return name.slice(name.lastIndexOf(".") + 1);
            } else {
                return false
            }
        }
    }

    const onClear = () => {
        blobUrl && window.URL.revokeObjectURL(blobUrl);
        setBlobUrl('');
        setFileName('');
        const freviewFileDom = document.getElementById('freviewFile')
        while (freviewFileDom?.firstChild) {
            freviewFileDom.removeChild(freviewFileDom.firstChild);
        }
    }

    const getFile = (code: string) => {
        setLoading(true);
        const fileName = getFileName(code);
        setFileName(fileName);
        const fileType = getFileType(code);
        if (!fileType) {
            return
        }

        Axios({
            url: downloadFile,
            method: "GET",
            responseType: 'blob',
            headers: {
                "Content-Type": " application/json;charset=UTF-8",
                "cattoken": token
            },
            params: { code: code }
        }).then(res => {
            const blob = new Blob([res.data])
            let reader = new FileReader();
            if (fileType === 'xls' || fileType === 'xlsx') {
                reader.readAsArrayBuffer(blob);
                reader.onload = async (e: any) => {
                    const box = document.createElement('div');
                    box.style.width = '94%';
                    box.style.minHeight = '700px';
                    box.style.marginLeft = '3%';
                    const myExcelPreviewer = jsPreviewExcel.init(box)
                    myExcelPreviewer
                        .preview(e.target.result)
                        .then(() => {
                            console.info('预览完成')
                            setLoading(false)
                        })
                        .catch((e) => {
                            console.info('预览失败', e)
                            setLoading(false)
                        })
                    const freviewFileDom = document.getElementById('freviewFile');
                    while (freviewFileDom?.firstChild) {
                        freviewFileDom.removeChild(freviewFileDom.firstChild);
                    }
                    freviewFileDom?.appendChild(box);
                }
            } else {
                reader.readAsText(blob);
                reader.onload = async (e: any) => {
                    if (e.target.result.indexOf('"statusCode"') >= 0) {
                        message.error(JSON.parse(e.target.result).message)
                    } else {
                        if (imgType.includes(fileType)) {
                            // 图片类预览
                            const blobUrl = URL.createObjectURL(blob);
                            var img = document.createElement('img');
                            img.onload = function () {
                                // 释放创建的URL对象
                                window.URL.revokeObjectURL(blobUrl);
                                setLoading(false);
                            };
                            img.src = blobUrl;
                            img.style.width = 'auto';
                            img.style.height = '300px';
                            const freviewFileDom = document.getElementById('freviewFile')
                            while (freviewFileDom?.firstChild) {
                                freviewFileDom.removeChild(freviewFileDom.firstChild);
                            }
                            freviewFileDom?.appendChild(img);
                        } else if (videoType.includes(fileType)) {
                            const blobUrl = URL.createObjectURL(blob);
                            // console.log('触发', videoType.includes(fileType), fileType, blobUrl)
                            // 创建video元素
                            const video = document.createElement('video');
                            video.controls = true;
                            video.style.width = '100%'; // 设置视频宽度为容器宽度
                            video.style.maxHeight = '100%'; // 设置视频最大高度为容器高度
                            // 当视频加载完成后释放URL对象
                            video.onended = function () {
                                URL.revokeObjectURL(blobUrl);
                            };
                            // 设置视频源
                            video.src = blobUrl;
                            setLoading(false)
                            // 获取预览容器元素
                            const freviewFileDom = document.getElementById('freviewFile')
                            while (freviewFileDom?.firstChild) {
                                freviewFileDom.removeChild(freviewFileDom.firstChild);
                            }
                            freviewFileDom?.appendChild(video);

                            // 将video元素添加到预览容器中
                            freviewFileDom?.appendChild(video);
                        } else if (fileType === 'pdf' || fileType === 'txt') {
                            const blob = new Blob([res.data], { type: fileTypeMap[fileType] })
                            const blobUrl = URL.createObjectURL(blob);
                            const iframe = document.createElement('iframe');
                            iframe.src = blobUrl;
                            setBlobUrl(() => blobUrl);
                            iframe.style.width = '100%';
                            iframe.style.minHeight = '700px';
                            iframe.onload = function () {
                                setLoading(false);
                            };
                            const freviewFileDom = document.getElementById('freviewFile');
                            while (freviewFileDom?.firstChild) {
                                freviewFileDom.removeChild(freviewFileDom.firstChild);
                            }
                            freviewFileDom?.appendChild(iframe);

                        } else if (fileType === 'doc' || fileType === 'docx') {
                            const blob = new Blob([res.data], { type: fileTypeMap[fileType] });
                            const box = document.createElement('div');
                            box.style.width = '80%';
                            box.style.minHeight = '700px';
                            box.style.marginLeft = '10%';
                            const freviewFileDom = document.getElementById('freviewFile');
                            while (freviewFileDom?.firstChild) {
                                freviewFileDom.removeChild(freviewFileDom.firstChild);
                            }
                            freviewFileDom?.appendChild(box);
                            let docx = require("docx-preview")
                            docx.renderAsync(blob, box).then(() => {
                                setLoading(false);
                                console.log("docx: finished")
                            }).catch(() => {
                                setLoading(false);
                                box.innerHTML = '文件解析失败,可下载预览';
                            })
                        } else {
                            setLoading(false);
                            const box = document.createElement('div');
                            box.style.width = '100%';
                            box.style.height = '300px';
                            box.style.textAlign = 'center';
                            box.style.fontSize = '18px';
                            box.style.lineHeight = '300px';
                            box.innerHTML = `暂不支持预览${fileType}格式,请自行下载预览`;
                            const freviewFileDom = document.getElementById('freviewFile');
                            while (freviewFileDom?.firstChild) {
                                freviewFileDom.removeChild(freviewFileDom.firstChild);
                            }
                            freviewFileDom?.appendChild(box);
                        }
                    }
                }
            }
        }).catch(err => {
            console.log(err);
            setLoading(false);
            const box = document.createElement('div');
            box.style.width = '100%';
            box.style.height = '300px';
            box.style.textAlign = 'center';
            box.style.fontSize = '18px';
            box.style.lineHeight = '300px';
            box.innerHTML = '解析文件失败了,可下载文件查看';
            const freviewFileDom = document.getElementById('freviewFile');
            while (freviewFileDom?.firstChild) {
                freviewFileDom.removeChild(freviewFileDom.firstChild);
            }
            freviewFileDom?.appendChild(box);
        })
    }

    useEffect(() => {
        if (code) {
            setModalVisit(true);
            if (getFileType(code)) {
                getFile(code);
            }
        } else {
            onClear();
        }

        return () => {
            onClear()
        }
    }, [code]);


    useEffect(() => {
        if (!modalVisit) {
            onClose();
        }
    }, [modalVisit])

    return (
        <ModalForm
            width="70vw"
            title="文件预览"
            open={modalVisit}
            style={{
                minHeight: '50vh',
                maxHeight: '90vh',
                overflowY: 'auto'
            }}
            onOpenChange={setModalVisit}
            submitter={false}
        >
            <Spin spinning={loading} delay={500} tip="加载中">
                <h1 style={{ fontSize: 18, marginBottom: 10 }}>{fileName}</h1>
                <div id="freviewFile" className="freviewFile" style={{ minHeight: 500 }}></div>
            </Spin>
        </ModalForm>
    )
}

export default PreviewFile;

使用组件PreviewFile

import PreviewFile from '@/components/PreviewFile';

    // 文件预览
    // 禁用鼠标右键的函数
    const handleContextMenu = useCallback((event: MouseEvent) => {
        event.preventDefault(); // 阻止默认的上下文菜单
    }, []);
    const [fileCode, setFileCode] = useState('');
    const onClosePreviewFile = () => {
        setFileCode('');
        document.removeEventListener('contextmenu', handleContextMenu);
    }



 <PreviewFile code={fileCode} onClose={onClosePreviewFile} />

code长什么样

code:bd55170a-e846-4cac-838f-b04d625g7e88/lalala.xlsx
大致张这个样吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值