业务层-hooks封装之usePreview

340 阅读3分钟

名称:usePreview

含义:

打开新页面,预览文档(excel、word等)

封装原因:

业务侧功能开发过程中,需要打开课件文档,但是不能让页面编码,充斥着大量的

window.open(jumpUrl, '_blank');

虽然一句编码很简单,只需要提供URL地址即可,但是场景使用不一样,需要做的操作也不太一样。

静态文件:
只需要提供服务器地址即可,相对比较简单

文档服务器:

打开相应文档,需要提供文件ID和文件名称,从文档服务器提取后打开

Hooks设计方式

usePreview.js

import { Config, Service } from '@basic-library'
import { Base64,uuid } from '@basic-utils'

const proxyConfig = "fileResource"

const getProxyUrl = (url) => {
    const protocol = window.location.protocol
    const host = window.location.host
    return `${protocol}//${host}/${proxyConfig}/onlinePreview?url=${url}`
}

/**
 * 获取字符串的哈希值
 * @param {String} str
 * @param {Boolean} caseSensitive
 * @return {Number} hashCode
 */
 const getHashCode =(str,caseSensitive)=>{
    if(!caseSensitive){
        str = str.toLowerCase();
    }
    // 1315423911=b'1001110011001111100011010100111'
    var hash  =   1315423911,i,ch;
    for (i = str.length - 1; i >= 0; i--) {
        ch = str.charCodeAt(i);
        hash ^= ((hash << 5) + ch + (hash >> 2));
    }
    return  (hash & 0x7FFFFFFF);
}

/**
 * @description: 获取下载文件地址
 * @param {*} item 文件相关对象信息
 * @return {*}
 */
const getFilePath = (item) => {
    const fileSeverIp = Config.BSConfig?.FilePreview
    const { fileId, fullfilename, oName } = item
    return fileSeverIp + Service.HParse('innerDownloadFile', {
        fileId,
        fullfilename,
        oName
    },false)
}

/**
 * @description: 文件地址 base64加密、转义
 * @param {*} path
 * @return {*}
 */
const formatPath = (path) => {
    return encodeURIComponent(Base64.toBase64(path))
}

/**
 * @description: 根据文件名称生成UUID文件名称
 * @param {*} fileId 文件id
 * @return {*}
 */
 const genFileName= (fileName,fileId) => {
    if(!fileName && fileName.lastIndexOf('.') == -1) return
    const getFileSuffix = name => name.split('.').pop()
    return `${getHashCode(fileId)}.${getFileSuffix(fileName)}`
}

/**
 * @description: 根据文件Id查找文件名称
 * @param {*} fileId 文件id
 * @return {*}
 */
const findFileName = async (fileId) => {
    const { returnObj } = await Service.useHttp('queryServiceFileDetail', { fileId })
    return returnObj.oFileName ?? ''
}

const usePreview = async (fileId, options) => {
    let fileName = ''
    let mode = ''
    if(!fileId) {
        new Error('文件ID为空!')
        return
    }

    if(typeof options == 'object' ){
        fileName = options.fileName ?? ''
        mode = options?.mode || ''
    }else{
        fileName = options
    }

    const fullfilename = !fileName ? await findFileName(fileId) : fileName

    if(!fullfilename) {
        new Error('查找文件名称为空!')
        return
    }

    const docPath = getFilePath({ fileId, fullfilename: genFileName(fullfilename,fileId), oName: fullfilename })

    const jumpUrl = getProxyUrl(formatPath(docPath))

    if(mode == 'path'){
        return jumpUrl
    }
    
    window.open(jumpUrl, '_blank');
};


export default usePreview

上述过程中,其实业务逻辑挺饶的

  • 需要支持获取解析拼装后的URL地址,主要目的是可能会给某个iframe用,页面内进行预览的情况
  • 基于打开浏览器新页签方式,采用window.open方式,对于请求的预览服务,后端直接采用第三方开源插件,支持图片、视频、各类文档等,暂未进行封装,所以请求预览服务时,参数会携带一个url参数,这个url参数指向内部文件服务器的文件地址,就像套娃一样。稍微感觉有点麻烦,但是现阶段没有办法。

业务使用

1、浏览器打开,最简单方式

usePreview('文件id')

这种方式,不传文件名称,内部会根据文件id,去文件服务查找文件名称,然后拼装参数,在请求预览服务进行展示

2、浏览器打开,传递文件名称

usePreview('文件id','文件1.doc')

或者

usePreview('文件id',{fileName:'文件1.doc'})

这种方式,传递文件名称,内部会直接使用,不在进行二次请求接口查找文件名称,后续逻辑和第1种一样

3、获取预览服务地址

usePreview('文件id',{mode:'path'})

返回是一个 URL地址,注意,文件名称,和第2种一样,如果不传递,会进行二次查找