沐游虞笔记
  • 前端面试题

    • HTML面试题汇总(无答案)
    • HTML面试题汇总
    • CSS 面试题汇总(无答案)
    • CSS 面试题汇总
    • javascript 面试题汇总(无答案)
    • javascript 面试题汇总
    • promise 面试题(无答案)
    • promise 面试题
    • 浏览器面试题汇总(无答案)
    • 浏览器面试题汇总
    • 网络面试题汇总(无答案)
    • 网络面试题汇总
    • 工程化面试题汇总(无答案)
    • 工程化面试题汇总
    • VUE面试题汇总(无答案)
    • VUE面试题汇总
  • 直播课文件

    • 静态页面学习指导
    • 属性的计算过程
    • 层叠继承规则总结
    • BFC
    • JS基础知识回顾
    • DOM 事件的传播机制
    • DOM 事件的注册和移除
    • 阻止事件默认行为
    • 基础领航考试题
    • 基础领航考试题(答案)
    • 2024前端发展
    • JS核心概念学习指导
    • 第三方库与工程化学习指导
    • Vue入门学习指导
    • vue进阶学习指导
    • 前端性能优化
  • 笔面试环节知识讲解

    • 目录
    • 图像处理
    • 图像处理(面试)
    • Webpack构建优化
    • Webpack构建优化(面试)
    • TTS性能优化
    • TTS性能优化(面试)
    • 实时协作
    • 实时协作(面试)
    • 网页复制图片到剪贴板
    • 网页复制图片到剪贴板(面试)
    • vite插件
    • vite插件(面试)
    • 表单数据同步与保持
    • 表单数据同步与保持(面试)
    • 优化虚拟列表
    • 优化虚拟列表(面试)
    • 微前端解决巨石应用
    • 微前端解决巨石应用(面试)
    • DNS解析与优化
    • DNS解析与优化(面试)
    • 前端监控
    • 前端监控(面试)
    • 12.跨标签页通信
    • 12.跨标签页通信(面试)
    • 13.Vite相关优化
    • 13.Vite相关优化(面试)
    • 14.计时器节流问题
    • 14.计时器节流问题(面试)
    • 15.多文件预览支持
    • 15.多文件预览支持(面试)
    • 16.defer优化白屏时间
    • 16.defer优化白屏时间(面试)
  • Vue3整体变化
  • Vue2响应式回顾
  • Vue3响应式变化
  • nextTick实现原理
  • 两道代码题
  • Vue运行机制
  • 渲染器核心功能
  • 事件绑定与更新
  • computed面试题
  • watch面试题
  • 图解双端diff
  • 图解快速dff
  • 最长递增子序列
  • 模板编译器
  • 模板编译提升
  • 组件name作用
  • 路由传参方式
  • 基础篇

    • 序章React介绍
    • JSX基础语法
    • React基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React--redux介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
    • Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • Reacti和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟DoM
    • React整体架构
    • React渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduleri调度普通任务
    • Scheduleri调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit工作流程
    • lane模型
    • React中的事件
    • Hooks原理
    • useStateuseReducer.
    • effect相关hook
    • useCallbackuseMemo
    • useRef
    • Update
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailoutContextAPl
    • 性能优化对日常开发启示
  • 前端监控概述
  • 错误监控
  • 数据上报
  • 页面性能监控
  • 用户行为收集与埋点
  • CSS3手册
  • HTML5手册
  • JavaScript语言提升

    • es补充
    • 事件循环
    • promise基础
    • Promise的链式调用
    • Promise的静态方法
    • async和await
    • Promise相关面试题
  • 网络

    • 客户端与服务器
    • 关于 Apifox 的使用
  • git文档
  • 工程化

    • CommonJS
    • ES module
    • npm文档(包管理)
    • Lass笔记
    • webpack工具
  • canvas详解
  • uinapp笔记
  • 自动化测试
  • oauth2令牌

    • 认识Oauth2
    • 三方应用实现github授权
    • 微信三方应用登录实现
    • 支付宝沙箱支付功能
  • 前端面试题

    • HTML面试题汇总(无答案)
    • HTML面试题汇总
    • CSS 面试题汇总(无答案)
    • CSS 面试题汇总
    • javascript 面试题汇总(无答案)
    • javascript 面试题汇总
    • promise 面试题(无答案)
    • promise 面试题
    • 浏览器面试题汇总(无答案)
    • 浏览器面试题汇总
    • 网络面试题汇总(无答案)
    • 网络面试题汇总
    • 工程化面试题汇总(无答案)
    • 工程化面试题汇总
    • VUE面试题汇总(无答案)
    • VUE面试题汇总
  • 直播课文件

    • 静态页面学习指导
    • 属性的计算过程
    • 层叠继承规则总结
    • BFC
    • JS基础知识回顾
    • DOM 事件的传播机制
    • DOM 事件的注册和移除
    • 阻止事件默认行为
    • 基础领航考试题
    • 基础领航考试题(答案)
    • 2024前端发展
    • JS核心概念学习指导
    • 第三方库与工程化学习指导
    • Vue入门学习指导
    • vue进阶学习指导
    • 前端性能优化
  • 笔面试环节知识讲解

    • 目录
    • 图像处理
    • 图像处理(面试)
    • Webpack构建优化
    • Webpack构建优化(面试)
    • TTS性能优化
    • TTS性能优化(面试)
    • 实时协作
    • 实时协作(面试)
    • 网页复制图片到剪贴板
    • 网页复制图片到剪贴板(面试)
    • vite插件
    • vite插件(面试)
    • 表单数据同步与保持
    • 表单数据同步与保持(面试)
    • 优化虚拟列表
    • 优化虚拟列表(面试)
    • 微前端解决巨石应用
    • 微前端解决巨石应用(面试)
    • DNS解析与优化
    • DNS解析与优化(面试)
    • 前端监控
    • 前端监控(面试)
    • 12.跨标签页通信
    • 12.跨标签页通信(面试)
    • 13.Vite相关优化
    • 13.Vite相关优化(面试)
    • 14.计时器节流问题
    • 14.计时器节流问题(面试)
    • 15.多文件预览支持
    • 15.多文件预览支持(面试)
    • 16.defer优化白屏时间
    • 16.defer优化白屏时间(面试)
  • Vue3整体变化
  • Vue2响应式回顾
  • Vue3响应式变化
  • nextTick实现原理
  • 两道代码题
  • Vue运行机制
  • 渲染器核心功能
  • 事件绑定与更新
  • computed面试题
  • watch面试题
  • 图解双端diff
  • 图解快速dff
  • 最长递增子序列
  • 模板编译器
  • 模板编译提升
  • 组件name作用
  • 路由传参方式
  • 基础篇

    • 序章React介绍
    • JSX基础语法
    • React基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React--redux介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
    • Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • Reacti和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟DoM
    • React整体架构
    • React渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduleri调度普通任务
    • Scheduleri调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit工作流程
    • lane模型
    • React中的事件
    • Hooks原理
    • useStateuseReducer.
    • effect相关hook
    • useCallbackuseMemo
    • useRef
    • Update
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailoutContextAPl
    • 性能优化对日常开发启示
  • 前端监控概述
  • 错误监控
  • 数据上报
  • 页面性能监控
  • 用户行为收集与埋点
  • CSS3手册
  • HTML5手册
  • JavaScript语言提升

    • es补充
    • 事件循环
    • promise基础
    • Promise的链式调用
    • Promise的静态方法
    • async和await
    • Promise相关面试题
  • 网络

    • 客户端与服务器
    • 关于 Apifox 的使用
  • git文档
  • 工程化

    • CommonJS
    • ES module
    • npm文档(包管理)
    • Lass笔记
    • webpack工具
  • canvas详解
  • uinapp笔记
  • 自动化测试
  • oauth2令牌

    • 认识Oauth2
    • 三方应用实现github授权
    • 微信三方应用登录实现
    • 支付宝沙箱支付功能
  • 必看导言

    • 就业的核心问题

      • 课件
    • 表现力的训练

      • 课件
  • 简历制作

    • 课件
    • 简历准备课堂笔记
    • 个人信息课堂笔记
    • 求职意向课堂笔记
    • 技术栈课堂笔记
    • 教育经历课堂笔记
    • 个人优势与评价课堂笔记
    • 工作经历课堂笔记
    • 项目
    • 项目亮难点问题

    • 项目经历

  • 项目准备

    • 课件
    • 课件
  • 技术重点

    • 划重点

    • 非技术环节
  • 简历投递和面试准备

    • 简历投递
    • 面试准备
  • 笔面试环节知识讲解
  • 项目准备
  • 难点攻关
  • 多文件预览支持
luzhichang
2024-09-18
目录

15-技术讲解

# 多文件预览支持

# 技术讲解

# 什么问题

相信大家都知道,如果我们上传或者下载了图片,都有在页面预览一下的需求,不过除了图片之外,其他的一些文件类型可能都有预览需求,比如视频,office相关文件,pdf,md,html等等文件都想实现预览效果该如何处理?

# 解决思路

不同类型的文件,文件运行时支持是不一样的

自有标签文件:图片、audio、video

纯文字的文件: markdown 、txt、代码

office类型的文件: docx、xlsx、ppt

embed引入文件:pdf、xmind

iframe:引入外部完整的网站

# 自有标签

浏览器本身就支持的图片、音视频标签这个好说,唯一要思考的是怎么更好的支持图片和视频预览。

我们要使用img标签src属性,拿到服务器的图片数据,传统的方式,要经历几个步骤才能拿到服务器的URL地址。

image-20240705113019581

但是我们只是想预览图片,本身浏览器平台也支持img,能不能直接在本地就拿到数据进行预览呢?理想的步骤应该是下面这个样子

image-20240705113637632

我们可以通过FileReader API与dataurl来解决预览图片问题,当然以前的方式也可以作为备案处理

<input type="file" />
<img src="" id="preview" alt="" />
  

const inp = document.querySelector('input');
inp.onchange = (e) => {
  const file = e.target.files[0];
  const reader = new FileReader();
  reader.onload = (e) => {
    const dataurl = e.target.result;
    preview.src = dataurl;
  };
  reader.readAsDataURL(file);
};

音视频预览的话,如果希望看到真实效果,也可以采用图片类似的方式,只不过需要使用URL.createObjectURL()

const inp = document.querySelector('input[type=file]');
inp.onchange = (e) => {
  const file = e.target.files[0];
  previewVideo(file)
};

function previewVideo(vdoFile){
  const vdo = document.createElement('video');
  // 静音, 由于浏览器限制,只有静音才能自动播放
  vdo.muted = true;
  // 让video自动播放
  vdo.autoplay = true;
  // createObjectURL可以把二进制的文件数据转换为浏览器可以播放的临时URL
  vdo.src = URL.createObjectURL(vdoFile);
  document.body.appendChild(vdo);
}

但是有时候我们可能需要的是可以预览视频帧图片即可,也就是说需要将视频帧截取出来,并画到界面上,这肯定就需要借助到cavans了

function captureFrame(vdoFile,time = 0) {
  return new Promise((resolve) => {
    const vdo = document.createElement('video');
    // 设置参数指定时间
    vdo.currentTime = time;
    // 静音, 由于浏览器限制,只有静音才能自动播放
    vdo.muted = true;
    // 让video自动播放
    vdo.autoplay = true;
    // createObjectURL可以把二进制的文件数据转换为浏览器可以播放的临时URL
    vdo.src = URL.createObjectURL(vdoFile);
    // 监听视频是否准备好,可以播放,因为视频是异步加载的
    vdo.oncanplay = () => {
      // 创建canvas,用于绘制视频帧
      const cvs = document.createElement('canvas');
      cvs.width = vdo.videoWidth;
      cvs.height = vdo.videoHeight;
      const ctx = cvs.getContext('2d');
      ctx.drawImage(vdo, 0, 0, cvs.width, cvs.height);

      // 返回blob对象和url
      cvs.toBlob((blob) => {
        const url = URL.createObjectURL(blob);
        resolve({
          blob,
          url,
        });
      });
    };
  });
}

const inp = document.querySelector('input[type=file]');

inp.onchange = (e) => {
  const file = e.target.files[0];
  for (let i = 0; i < 10; i++) {
    captureFrame(file, i * 1).then((result) => {
      previewImage(result.url);
    });
  }
};

function previewImage(url) {
  const img = document.createElement('img');
  img.src = url;
  document.body.appendChild(img);
}

当然,也能使用原始的方式,先上传到服务器,服务器返回保存的URL地址,页面直接通过<audio />或者<video />标签打开服务器地址保存的URL地址就行了

# 纯文字文件

一般都是文本文件txt,或者代码,当然我这里把markdown文件也包含了进去。这几个处理思路都差不多,纯文本没什么可多说的,直接读取即可,代码和markdown文件最需要处理的就是样式和代码着色,这些都有专门的第三方库处理,比如就markdown文件而言,大家可以用用下面的一些第三方库

marked : 用来解析markdown
marked-highlight : 用来代码块高亮
github-markdown-css : 设置markdown样式
highlight.js:语法高亮

类似于:

<script lang="ts" setup>
import { ref } from "vue";
import md_content from "../assets/vite-12.md?raw";
import { onMounted } from "vue";
import { Marked } from "marked";
import { markedHighlight } from "marked-highlight";
import hljs from "highlight.js";
//高亮代码样式
import "highlight.js/styles/github-dark.css";

const contentDetails = ref();

const marked = new Marked(
  markedHighlight({
    langPrefix: "hljs language-",
    highlight(code, lang) {
      const language = hljs.getLanguage(lang) ? lang : "shell";
      return hljs.highlight(code, { language }).value;
    },
  })
);

onMounted(() => {
  contentDetails.value = marked.parse(md_content);
});
</script>

<template>
  <div>
    <div v-html="contentDetails"></div>
  </div>
</template>

# office类型文件

这类文件要去重复造轮子就没那个必要了,也没有那个时间成本,纯前端的手段其实很有限,有一些第三方库可以实现效果,不过也都不是太成熟,支持比较有限,有需要的同学可以了解一下,比如vue-office (opens new window)。用起来很简单

使用kkfileview (opens new window),这就是一个在线预览解决方案,直接支持众多类型文件预览,而且开源协议也是Apache2.0,其实本身就可以一站式帮我们解决很多预览问题,不过需要私有化部署,需要单独的服务器处理,本身也不是纯前端的处理,还是需要把文件上传到自己的服务器。

这种做法其实微软本身就给我们提供了免费的解决方案 (opens new window):

https://view.officeapps.live.com/op/view.aspx?src=&lt;文档位置>

也就是说,我们只需要有一个公网的office文件地址,拼接上这个网址,就直接能解决这个问题。

不过最大的问题是,这个只能预览不能修改

如果希望修改的话,还可以使用onlyoffice (opens new window),或者云服务,比如阿里云的智能媒体管理 IMM (opens new window)

# embed引入文件

pdf文件现在浏览器直接支持,也不用纠结太多,无论是embed还是iframe都是直接引入

<embed src="http://xxx.xxx.com/record/xxx.pdf" />
<iframe
  src="http://xxx.xxx.com/record/xxx.pdf"
  width="100%"
  height="400"
/>

xmind文件的情况和office差不多,前端虽然有 xmind-embed-viewer (opens new window)这个第三方库,但是还是需要有你xmind文件的公网地址,,加载的速度很慢,建议添加loading处理,而且在界面引用的话,xmind格式不对会引起一堆错误

<template>
  <div id="xmind-container">
    <Loading v-if="showLoading" />
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref } from 'vue'
import Loading from "./Loading.vue"

const showLoading = ref(true)

const props = defineProps({
  url: {
    type: String,
    required: true
  }
})

onMounted(async () => {
  const { XMindEmbedViewer } = await import('xmind-embed-viewer')
  const viewer = new XMindEmbedViewer({
    el: '#xmind-container', // HTMLElement | HTMLIFrameElement | string
    // 如果在中国大陆境内速度慢,可以添加的参数 `region: 'cn'` 改为使用 xmind.cn 的图库作为依赖。
    region: 'cn' //optional, global(default) or cn
  })
  viewer.setStyles({
    width: '100%',
    height: '100%'
  })
  const callback = () => {
    showLoading.value = false
    viewer.removeEventListener('map-ready', callback)
  }
  viewer.addEventListener('map-ready', callback)
  fetch(props.url)
    .then(res => res.arrayBuffer())
    .then(file => {
      viewer.load(file)
    })
    .catch(_err => {
      showLoading.value = false
      viewer.removeEventListener('map-ready', callback)
    })
})
</script>

<style>
#xmind-container {
  display: flex;
  height: 100%;
  align-items: center;
  justify-content: center;
}
</style>

引入:

import XmindViewer from "./components/XmindViewer.vue";

// 还是需要公网的资源地址
<XmindViewer url="http://xxx.xxx.com/xxx/test-1.xmind" />

# 解决细节

# 1、data url (opens new window)

Data URL 由四个部分组成:前缀(data:)、指示数据类型的 MIME 类型、如果非文本则为可选的 base64 标记、数据本身:

data:[<mediatype>][;base64],<data>

mediatype 是个 MIME 类型 (opens new window)的字符串,例如 'image/jpeg' 表示 JPEG 图像文件。如果被省略,则默认值为 text/plain;charset=US-ASCII。

JS中对Base64编码的处理 (opens new window)

<input type="file" />
<img src="" id="preview" alt="" />
  
const inp = document.querySelector('input');
inp.onchange = (e) => {
  const file = e.target.files[0];
  const reader = new FileReader();
  reader.onload = (e) => {
    const dataurl = e.target.result;
    preview.src = dataurl;
  };
  reader.readAsDataURL(file);
};

# 2、URL.createObjectURL() (opens new window)

# 3、公网地址的资源权限问题

office文件,pdf,xmind这些处理其实都需要引入你公网地址的资源路径,如果这些文件或者资源路径比较敏感的话,直接暴露出来并不是太好,有没有办法隐藏一下

可以添加一个BFF(Backend for Frontend)层来隐藏真实链接,BFF层可以作为前端与后端服务之间的中介,控制和验证用户的请求,然后从后端获取资源并返回给前端,而无需暴露真实的资源链接,简单来说,就是由BFF层做转发并且做统一的权限验证。

# 4、iframe引入外部完整的网站

有些网站设置了X-Frame-Options不允许其他网站嵌入,X-Frame-Options 是一个 HTTP 响应头,用于控制浏览器是否允许一个页面在 <frame>、 <iframe>、 <embed>、 或 <object> 中被嵌入。

X-Frame-Options有以下三种配置:

  • DENY:完全禁止该页面被嵌入到任何框架中,无论嵌入页面的来源是什么。
  • SAMEORIGIN:允许同源的页面嵌入该页面。
  • ALLOW-FROM uri:允许指定的来源嵌入该页面。这个选项允许你指定一个 URI,只有来自该 URI 的页面可以嵌入当前页面。

但是无论是哪种配置,我们作为非同源的网站,都无法将其嵌入到页面中

比如:

<!-- Refused to display 'https://www.ted.com/' in a frame because it set 'X-Frame-Options' to 'sameorigin'. -->
  <iframe
    src="https://www.ted.com/"
    title="iframe Example 1"
    width="400"
    height="300"
  >
  </iframe>

简单来说,x-frame-options就和同源策略一样,都是服务器返回了数据/页面,但是浏览器不予展示。 所以想消除x-frame-options在浏览器中的行为,最好的办法就是把这个响应头去掉。那么浏览器就会正常的展示对应的数据/页面。那么怎么去掉,就需要知道一些原理性问题:

# 1、x-frame-options哪来的?

x-frame-options是目标服务器在返回response时,人为在其响应头中添加的。这个过程就是:

  1. 客户端(比如浏览器)发起请求
  2. 目标服务器响应该请求
  3. 目标服务器返回真实数据 + 设置response响应头x-frame-options: sameorigin
  4. 客户端发现x-frame-options为sameorigin,不展示真实数据

# 2、如何去掉x-frame-options?

了解x-frame-options如何被添加后,解决思路就是,如何在3、4中间,将response响应头x-frame-options直接干掉,这样浏览器就察觉不到x-frame-options的存在,进而正常展示

通常采取的办法就是正向代理:在3、4中间,架设一个处理节点,这个处理节点可以改写目标服务器的响应头,将改写后的响应头再回传给客户端,即处理节点删除了响应头中的x-frame-options。

  1. 如果你的web服务器是nginx,可以在nginx中,利用nginx的语法,删除response的响应头x-frame-options
  2. 如果很难去操作线上的nginx配置,可以搭建一个BFF层,又或者直接让后端处理,在node服务器中删除响应的x-frame-options请求头

如果我们仅仅只是想测试,有个chrome插件Ignore X-Frame headers (opens new window),可以帮我们测试这个问题

Theme by Vdoing | Copyright © 2021-2024 蜀ICP备2024068710号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式