当前位置: 首页 > news >正文

实际开发中,常见pdf|word|excel等文件的预览和下载

实际开发中,常见pdf|word|excel等文件的预览和下载

    • 背景
    • 相关类型数据之间的转换
      • 1、File转Blob
      • 2、File转ArrayBuffer
      • 3、Blob转ArrayBuffer
      • 4、Blob转File
      • 5、ArrayBuffer转Blob
      • 6、ArrayBuffer转File
    • 根据Blob/File类型生成可预览的Base64地址
    • 基于Blob类型的各种文件的下载
    • 各种类型文件的预览及其效果
      • 1、当前使用的node版本
      • 2、 业务场景
      • 3、图片类型预览
        • 3.1、安装依赖
        • 3.2、ImagePreview.vue
        • 3.3、效果
      • 4、Excel文件的预览
        • 4.1、依赖安装
        • 4.2、ExcelPreview.vue
        • 4.3、预览效果
      • 5、word文件的预览
        • 5.1、依赖安装
        • 5.2、WordPreview.vue
        • 5.3、预览效果
      • 6、pdf文件的预览
        • 6.1、依赖安装
        • 6.2、PdfPreview.vue
        • 6.3、预览效果
      • 7、json文件的预览

背景

实际开发中,大部分文件的预览会以流的方式传输,前端通过Element等UI库提供的上传组件传给后端File类型数据, 后端返回给前端Blob/ArrayBuffer类型数据 , 前端最终借助各种第三方工具或者自定义tool方法, 实现各种类型文件的下载或者预览. 少部分的会以文件地址的方式进行传输, 那么我们直接访问那个文件url即可.

相关类型数据之间的转换

1、File转Blob

export function fileToBlob(file: File) {return new Promise((resolve, reject) => {const reader = new FileReader();reader.onload = () => {const arrayBuffer: any = reader.result;const blob = new Blob([arrayBuffer], { type: file.type });resolve(blob);};reader.onerror = reject;reader.readAsArrayBuffer(file);});
}

在这里插入图片描述

2、File转ArrayBuffer

export function fileToArrayBuffer(file: File) {return new Promise((resolve, reject) => {const reader = new FileReader();reader.onload = () => {const arrayBuffer: any = reader.result;resolve(arrayBuffer);};reader.onerror = reject;reader.readAsArrayBuffer(file);});
}

3、Blob转ArrayBuffer

export function blobToArrayBuffer(blob) {return new Promise((resolve, reject) => {const reader = new FileReader();reader.onload = () => resolve(reader.result);reader.onerror = reject;reader.readAsArrayBuffer(blob);});
}

4、Blob转File

export function blobToFile(blob, fileName, fileType) {return new File([blob], fileName, { type: fileType })
}

5、ArrayBuffer转Blob

export function arrayBufferToBlob(arrayBuffer, blobType = 'application/octet-stream') {const blob = new Blob([arrayBuffer], { type: blobType  });return blob;
}

6、ArrayBuffer转File

export function arrayBufferToFile(arrayBuffer, fileName, fileType = 'text/plain') {const file= new File([arrayBuffer], fileName, { type: fileType  });return file;
}

根据Blob/File类型生成可预览的Base64地址

有些第三方预览工具不识别Blob/File, 如viewerjsv-viewer 预览图片的时候,是需要图片对应的src的,而不是Blob/File

export function createUrlByBlobOrFile(data: any) {return new Promise((resolve, reject) => {const reader = new FileReader();reader.onload = () => {resolve(reader.result);};reader.onerror = reject;reader.readAsDataURL(data);});
}

基于Blob类型的各种文件的下载

下载的文件响应类型可打印FIle/Blob对象查看,可执行:downloadFileUtil(fileBlob, fileBlob.type, fileBlob.fileName)

export function downloadFileUtil(data: Blob, responseType: string, fileName: any = new Date().valueOf()) {const blob = new Blob([data], { type: responseType });// 创建一个<a></a>标签let a: HTMLAnchorElement | null = document.createElement('a');const blobUrl = window.URL.createObjectURL(blob);a.href = blobUrl;a.download = fileName;a.style.display = 'none';document.body.appendChild(a);a.click();a.remove();// 释放createObjectURL创建的资源window.URL.revokeObjectURL(blobUrl);
}

各种类型文件的预览及其效果

个别预览的第三方插件库,需要使用特定的某些版本,当前指定的版本库都是可用的。

1、当前使用的node版本

在这里插入图片描述

2、 业务场景

  • 用户通过上传组件上传附件

用户从本地上传的附件拿到的类型是File, 保存之后, 拿到的就是文件列表项对应的Blob类型。
在这里插入图片描述

3、图片类型预览

图片类型预览使用的是v-viewerviewerjs, 可支持的预览图片类型有:jpg, jpeg, png, gif

3.1、安装依赖
yarn add v-viewer@^3.0.21 viewerjs@^1.11.7
3.2、ImagePreview.vue

v-viewerviewerjs 可以通过指令、组件和api三种方式实现预览。 实际开发中,基本上都是使用的是Blob类型,Blob类型转换为Base64地址后, 是不能通过import { api as viewerApi } from 'v-viewer';的方式预览的,尽管api的方式很简单,但它貌似只是支持本地文件URL/服务器文件URL。

通过使用viewer组件,借助img标签可以识别Base64图片路径,从而通过点击img列表,实现图片预览

<template><div class="image-preview"><viewer :images="props.images" class="v-viewer"><imgv-for="(imgItem, index) in props.images":key="index"class="view-img-item":src="imgItem.url":alt="imgItem.name":title="imgItem.name"/></viewer><div class="auto-close-preview-com"><Close class="close-icon" @click="closeImgPreviewFn" /></div></div>
</template><script lang="ts" setup>
import 'viewerjs/dist/viewer.css';
import { component as Viewer } from 'v-viewer';
import { onMounted } from 'vue';
import { ElMessage } from 'element-plus';const props = defineProps({images: {type: Array as any,  // images存储的是Blob转成Base64的数组,类型转换上文createUrlByBlobOrFile可实现default: () => [],},
});
const emits = defineEmits(['closeImgPreview']);function closeImgPreviewFn() {emits('closeImgPreview');
}
onMounted(() => {ElMessage.info('点击图片列表可预览~');
});
</script><style lang="css" scoped>
.image-preview {position: fixed;left: 0;top: 0;right: 0;bottom: 0;z-index: 9998;background-color: rgb(0 0 0 / 70%);.v-viewer {width: 100%;height: 100%;.view-img-item {width: 250px;height: 250px;margin-right: 20px;}}.auto-close-preview-com {position: absolute;-webkit-app-region: no-drag;background-color: rgb(0 0 0 / 50%);border-radius: 50%;cursor: pointer;height: 80px;overflow: hidden;right: -40px;top: -40px;transition: background-color 0.15s;width: 80px;color: #ffffff;.close-icon {bottom: 15px;left: 15px;position: absolute;background-position: -260px 0;font-size: 0;height: 20px;line-height: 0;width: 20px;}}
}
</style>
3.3、效果

在这里插入图片描述

4、Excel文件的预览

Excel文件预览使用的是xlsx 插件库, 可支持类型有:xls, xlsx

4.1、依赖安装
yarn add xlsx@^0.18.5
4.2、ExcelPreview.vue
<template><div class="xlsx-preview-box"></div>
</template><script lang="ts" setup>
import { onMounted } from 'vue';
// XLSX: 无法预览docx文件, 预览pdf也会乱码  只能预览xlsx文件
import * as XLSX from 'xlsx';const props = defineProps({fileBlob: {type: Blob,default: () => null,},
});onMounted(() => {if (props.fileBlob) {const reader = new FileReader();// 通过readAsArrayBuffer将blob转换为ArrayBufferreader.readAsArrayBuffer(props.fileBlob);reader.onload = (event: any) => {// 读取ArrayBuffer数据变成Uint8Arrayconst data = new Uint8Array(event.target.result);// 这里的data里面的类型和后面的type类型要对应const workbook = XLSX.read(data, { type: 'array' });const sheetNames = workbook.SheetNames; // 工作表名称const worksheet = workbook.Sheets[sheetNames[0]];const html = XLSX.utils.sheet_to_html(worksheet);document.getElementsByClassName('xlsx-preview-box')[0].innerHTML = html;};}
});
</script><style lang="css">
.xlsx-preview-box {width: 100%;height: 100%;overflow: auto;table {width: 100%;border-spacing: 0;tr {height: 40px;font-size: 14px;color: #666666;line-height: 14px;font-weight: 400;}tr:first-child {background-color: #ececec !important;height: 60px;font-size: 16px;color: #666666;font-weight: 700;}td {min-width: 80px;text-align: center;border: 1px solid #cccccc;}tr:nth-child(2n) {background-color: #fafafa;}tr:nth-child(2n + 1) {background-color: #ffffff;}}
}
</style>
4.3、预览效果

在这里插入图片描述

5、word文件的预览

word文件预览使用的是docx-preview 插件库, 可支持类型有:doc, docx

5.1、依赖安装
yarn add docx-preview@0.3.0

docx-preview 需要是0.3.0版本,最新的0.3.3版本会报docx-preview类型错误。且最新的版本解析的blob文件类型和0.3.0版本不一致,最新版本还会预览失败:报(Can’t find end of central directory : is this a zip file ? If it is, see)。

5.2、WordPreview.vue
<template><div ref="wordPreviewRef" class="word-preview"></div>
</template><script lang="ts" setup>
import { ref, nextTick } from 'vue';
// docx-preview 需要是0.3.0版本,最新的0.3.3版本会报docx-preview类型错误
// 且最新的版本解析的blob类型和0.3.0版本不一致
// 最新版本还会预览失败:报(Can't find end of central directory : is this a zip file ? If it is, see)
import { renderAsync } from 'docx-preview';const props = defineProps<{wordBlob: any;
}>();const wordPreviewRef = ref({});nextTick(() => {renderAsync(props.wordBlob, // blob 的type: application/vnd.openxmlformats-officedocument.wordprocessingml.documentwordPreviewRef.value as HTMLElement, // HTMLElement 渲染文档内容的元素,);
});
</script><style lang="scss" scoped>
.word-preview {width: 100%;height: 100%;overflow: auto;
}
</style>
5.3、预览效果

在这里插入图片描述

6、pdf文件的预览

pdf文件预览使用的是pdfjs-dist 插件库, 可支持类型有:pdf

6.1、依赖安装
yarn add pdfjs-dist@2.16.105

pdfjs-dist 底层是pdfjs。不建议使用打包后的mjs类型的版本包。因为不支持线上环境对GlobalWorkerOptions.workerSrc的支持。具体的是:本地可以引入node_module路径,但是正式环境没这个路径;如果把对应的pdf.worker.min.mjs放到assets下,会报错:Failed to resolve module specifier '@/assets/pdfjs/pdf.worker.min.mjs; 如果放到public下,会报错Failed to load module script, public目录文件不会被编译,浏览器无法识别mjs文件

6.2、PdfPreview.vue
<template><div class="pdf-preview"><!-- block: 避免一个视图显示多个canvas页 --><canvasv-for="pageIndex in pdfPages":id="`pdf-canvas-` + pageIndex"ref="pdfPreviewRef":key="pageIndex"style="display: block"></canvas></div>
</template><script lang="ts" setup>
import { ref, onMounted, nextTick, reactive } from 'vue';// import 'pdfjs-dist/web/pdf_viewer.css';
// 4.5.136版本
// import * as pdfjsLib from 'pdfjs-dist'; // /legacy/build/pdf.js
// import * as pdfjsViewer from 'pdfjs-dist/web/pdf_viewer.js';
import 'pdfjs-dist/web/pdf_viewer.css';
import * as pdfjsLib from 'pdfjs-dist';
import { blobToArrayBuffer } from '@/utils/tools';const props = defineProps<{pdfBlob: any;
}>();const pdfPreviewRef = ref({});
// pdf页数
const pdfPages = ref(0);
// pdf缩放比例
const pdfScale = ref(2.5); // 可以控制canvas的宽高
// pdf文档流,
// 这个不能使用ref,使用ref会报错: Cannot read from private field
let pdfDoc = reactive<any>({});const renderPdf = (num) => {pdfDoc.getPage(num).then((page) => {const canvasId = `pdf-canvas-${num}`;const canvas: any = document.getElementById(canvasId);const ctx = canvas?.getContext('2d');const dpr = window.devicePixelRatio || 1;const bsr =ctx.webkitBackingStorePixelRatio ||ctx.mozBackingStorePixelRatio ||ctx.msBackingStorePixelRatio ||ctx.oBackingStorePixelRatio ||ctx.backingStorePixelRatio ||1;const ratio = dpr / bsr;const viewport = page.getViewport({ scale: pdfScale.value });canvas.width = viewport.width * ratio;canvas.height = viewport.height * ratio;canvas.style.width = `${viewport.width}px`;canvas.style.height = `${viewport.height}px`;ctx.setTransform(ratio, 0, 0, ratio, 0, 0);const renderContext = {canvasContext: ctx,viewport: viewport,};page.render(renderContext);if (num < pdfPages.value) {renderPdf(num + 1);}});
};// 获取pdf文档流与pdf文件的页数
const loadFile = async () => {//  string | URL | TypedArray | ArrayBuffer | DocumentInitParametersconst pdfArrayBuffer: any = await blobToArrayBuffer(props.pdfBlob);const loadingTask = pdfjsLib.getDocument(pdfArrayBuffer);loadingTask.promise.then((pdf) => {pdfDoc = pdf; // 获取pdf文档流pdfPages.value = pdf.numPages; // 获取pdf文件的页数nextTick(() => {renderPdf(1);});});
};onMounted(async () => {// 正式环境找不到node_modules// pdfjsLib.GlobalWorkerOptions.workerSrc =//   '../../../node_modules/pdfjs-dist/build/pdf.worker.min.mjs';// 放在assets下: Failed to resolve module specifier '@/assets/pdfjs/pdf.worker.min.mjs// pdfjsLib.GlobalWorkerOptions.workerSrc = '@/assets/pdfjs/pdf.worker.min.mjs';// const baseurl = window.location.origin + window.location.pathname; // 本地路径// ${baseurl}pdfjs/pdf.worker.min.mjs 静态服务访问的返回的是流// pdfjsLib.GlobalWorkerOptions.workerSrc = `${baseurl}pdfjs/pdf.worker.min.mjs`; // Failed to load module scriptpdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs/pdf.worker.js';  // “pdfjs/”不能写成“/pdfjs/”, 前者是相对路径, 后者是绝对路径(相对线上环境服务器)loadFile();
});
</script>
<style lang="scss" scoped>
.pdf-preview {width: 100%;height: 100%;overflow: auto;
}
</style>
6.3、预览效果

在这里插入图片描述

7、json文件的预览


http://www.mrgr.cn/news/82361.html

相关文章:

  • 第 31 章 - 源码篇 - Elasticsearch 写入流程深入分析
  • 鸿蒙应用开发搬砖经验之—使用DevTools工具调试前端页面
  • Elasticsearch:Lucene 2024 年回顾
  • primevue的<Menu>组件
  • 30分钟学会css
  • 32单片机从入门到精通之软件编程——中断处理(九)
  • 前言(1)
  • flink cdc oceanbase(binlog模式)
  • 二、AI知识(神经网络)
  • Speedtest 测试客户的上/下行带宽
  • 泊松融合调研
  • 机器学习 学习知识点
  • Spark-Streaming有状态计算
  • Qt之简易音视频播放器设计(十五)
  • JAVA-制作小游戏期末实训
  • 服务端错误的处理和web安全检测
  • 六年之约day5
  • 软件项目的灵魂拷问:“要做什么?”和“做成了什么?”
  • Win11电脑Cursor默认打开markdown文件,如何修改markdown文件默认打开方式为Typora?
  • 鸿蒙开发汇总
  • HarmonyNext 鸿蒙开发中,在H5 页面如何下载图片保存到媒体库。
  • 使用Diffusion Models进行图像超分辩重建
  • CodeBlocks编程-C语言学习总结
  • 通过blob请求后端导出文件
  • iperf3 测试云服务性能
  • 静态初始化块与非静态初始化块