一行 js 代码预览图片、视频、文件?

3,548 阅读3分钟

用最简单的使用方式,实现大多数类型资源的预览。

IPreview.previewUuid('xxxx-xxxx-xxxx-xxxx');

简介

一行代码实现预览 是我在公司内搭建的前端组件库中的一个功能。目的是解决预览方式不统一预览不同类型资源困难预览实现代码混乱的问题。这个功能从提出到落地应用,极大提高了我的开发效率(更多摸鱼时间)!

tips: 需要注意的是,这个功能依赖于云对象OSS存储,这也是大多数企业存储文件的方式,个人项目需要根据实现原理另行封装改造。

预览演示

此处为我部署在公司内网的文档站点,演示各类资源的预览效果。

4k.gif

实现原理

这里用简单代码展示原理,方便理解。实际开发中会进行大量优化,以及调整预览UI、预览功能等等。

3.1 根据uuid获取url

文件存储服务依赖uuid来区分文件,避免同名文件覆盖问题。一般在公司前端基建中已经封装好了工具函数,可以通过uuid获取文件地址。类似下面这种格式:

export async function getUrlByUuid (uuid: string): Promise<string> {
    // ...
}

3.2 通过url计算预览信息

从云存储厂商返回的url中,我们能计算出很多信息。我总结了下有这些:fileNamefileExtfileType,将它们统一放在名为预览信息的对象里。

interface PreviewInfo {
    url: string; // oss文件地址
    fileName: string; // 文件名称
    fileExt: string; // 文件后缀 (例如:"doc")
    fileType: 'img' | 'file' | 'video'; // 文件类型
}

export async function getPreviewInfoFromUrl (url: string): PreviewInfo {
    // 根据url计算预览信息
    // ...
}

3.3 预览图片

为了用于预览时缩放、移动图片的功能,我选择基于antd的Image组件进行封装,并通过函数调用的方式预览。

图片预览模板组件:

function PreviewImage ({src, onClose}) {
    return (
       <Image
         preview={{
           src,
           visible: true,
           onVisibleChange: value => {
              if (!value) {
                onClose?.()
              }
           }
         }}
       />
    )
}

预览图片:

// React 16
type Unmount = () => void;

// 打开预览弹窗
function openPreview (Template: React.FC, previewInfo: PreviewInfo): Unmount {
  let div = document.createElement('div');
  let el = React.createElement(Template, {
      src: previewInfo.url,
      onClose: unmount
  })
  document.body.appendChild(div);
  
  // 将React元素渲染到div,让预览组件生效
  ReactDOM.render(el, div);
  
  // 卸载
  function unmount () {
    if (!div) return;
    ReactDOM.unmountComponentAtNode(div);
    div.remove();
    div = null;
    el = null;
  }
  
  return unmount;
}

// 预览图片
function previewImgUrl (previewInfo: PreviewInfo) {
   openPreview(PreviewImage, previewInfo)
}

3.4 预览文件

预览文件这一步需要注意的是:需要获取可预览的url地址,否则文件有些会直接下载,例如doc。以doc文件可预览地址举例,需要公司事先拥有预览文件的在线网址。如果没有的话,需要去搭建一个类似的站点提供服务,方便后期一处维护所有项目生效。类似于这样的方式:

https://my.previewfile.com?url=【encodeURIComponent(oss地址)】

预览Iframe模板组件:

import { Modal } from 'antd'
function PreviewIframe ({src, onClose}) {
    const [visible, setVisible] = useState(true);
    return (
        <Modal
          visible={visible}
          title="预览文件"
          afterClose={onClose}
          onCancel={() => setVisible(false)}
          footer={null}
        >
          <iframe src={src} style={{width: '80vw',height: '80vh'}}/>
        </Modal>
    )
}

预览文件

// React 16
function previewFileUrl (previewInfo: PreviewInfo): Unmount {
   return openPreview(PreviewIframe, previewInfo)
}

3.5 预览视频

预览视频,我这里选择的是 aliplayer-react,将其封装成视频弹窗模板。

预览视频模板组件:

import { Modal } from 'antd';
import Player from 'aliplayer-react';

// Video默认配置项
const INITIAL = {
  source: '',
  width: '100%',
  height: '100%',
  autoplay: true,
  isLive: false,
  rePlay: false,
  playsinline: true,
  preload: true,
  controlBarVisibility: 'hover',
  useH5Prism: true,
  components: [
    {
      name: 'RateComponent',
      type: Player.components.RateComponent,
    },
  ],
};


function PreviewVideo ({src, onClose}) {
    const [visible, setVisible] = useState(true);
    
    // 配置添加视频地址
    const videoConfig = useMemo(() => {
        return {
            ...INITIAL,
            source: src
        }
    }, [])
    
    return (
        <Modal
          visible={visible}
          title="预览文件"
          afterClose={onClose}
          onCancel={() => setVisible(false)}
          footer={null}
        >
          <Player config={videoConfig} />
        </Modal>
    )
}

预览文件

// React 16
function previewVideoUrl (previewInfo: PreviewInfo) {
   openPreview(PreviewVideo, previewInfo)
}

previewUuid 代码实现


class Preview {
   // 其他功能
   // ... 
   
   // 预览文件oss地址
   public async previewUuid (uuid: string) {
       const url = await getUrlByUuid(uuid);
       return this.previewUrl(url)
   }
  
  // 预览文件uuid
   public async previewUrl (ossUrl: string) {
       const previewInfo = await getPreviewInfoFromUrl(ossUrl);
       
       // 预览不同文件
       switch (previewInfo.fileType) {
           case 'img':  // 预览图片
               previewImgUrl(previewInfo)
               break; 
           case 'file': // 预览文件
               previewFileUrl(previewInfo)
               break; 
           case 'video': // 预览视频
               previewVideoUrl(previewInfo)
               break; 
           default:
               message.warn(`.${previewInfo.fileExt}格式文件暂不支持预览!`)
               break;
       }
   }
}