万能预览器:'image'|'docx'|'xlsx'|'pdf'。任意web框架

29 阅读1分钟
/** 按需安装依赖: @js-preview/docx @js-preview/excel @js-preview/pdf viewerjs */

/** 预览文档(支持性:看ext参数类型)
 * @param {HTMLElement} dom 挂载节点
 * @param {string} url 文件地址
 * @param {undefined|'image'|'docx'|'xlsx'|'pdf'} ext 文件类型(极少情况下需要传)
 */
export default function preview(dom, url, ext) {
    return new Promise(async (resolve, reject) => {
        try {
            if (!dom) throw new Error("缺少挂载的dom");
            if (!url) throw new Error("缺少url");

            if (!ext) ext = getExt(url);
            if (!ext) {
                const response = await fetch(url);
                const blob = await response.blob();
                ext = blob.type.split('/')[1];
                url = URL.createObjectURL(blob);
            }
            if (!ext) throw new Error("缺少文件类型");

            if (ext === 'docx') {
                const JsPreview = (await import('@js-preview/docx')).default;
                await import('@js-preview/docx/lib/index.css');
                const Previewer = JsPreview.init(dom);
                return resolve(await Previewer.preview(url));
            }
            if (ext === 'xlsx') {
                const JsPreview = (await import('@js-preview/excel')).default;
                await import('@js-preview/excel/lib/index.css');
                const Previewer = JsPreview.init(dom);
                return resolve(await Previewer.preview(url));
            }
            if (ext === 'pdf') {
                const JsPreview = (await import('@js-preview/pdf')).default;
                const Previewer = JsPreview.init(dom);
                return resolve(await Previewer.preview(url));
            }
            if (['image', 'jpeg', 'jpg', 'png', 'gif', 'bmp', 'webp', 'avif', 'svg', 'apng', 'tiff'].includes(ext)) {
                const Viewer = (await import('viewerjs')).default;
                await import('viewerjs/dist/viewer.css');
                
                const el = Object.assign(document.createElement('img'), {
                    src: url,
                    style: 'width: 100%;height: 100%;object-fit: contain;'
                });
                dom.insertBefore(el, dom.firstChild);

                return resolve(new Viewer(dom, {
                    viewed() {
                        Viewer.zoomTo(1);
                    },
                }));
            }
            throw new Error(`不支持的文件类型[${ext}]:${url}`);
        } catch (error) {
            reject(error);
        }
    })
}

/** 获取URL文件扩展名(小写,不含点),支持: 标准、编码、base64Data 的URL
 * @param {string} url
 */
export function getExt(url) {
    // data URL: data:image/png;base64,xxx
    if (url.startsWith('data:')) {
        const m = url.match(/^data:([^;,]+)/);
        return m ? m[1].split('/')[1] || '' : '';
    }
    // 解码 → 清理参数 → 提取文件名 → 匹配扩展名
    try { url = decodeURIComponent(url); } catch { }
    const match = url.split('?')[0].split('#')[0].split('/').pop()?.match(/\.([a-z0-9]{1,10})$/i);
    return match ? match[1].toLowerCase() : '';
}