写了个弹窗,传请求参数,在里面请求后端数据,数据流为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
大致张这个样吧