diff --git a/src/previewsetup.ts b/src/previewsetup.ts index 294bb05..2ee393b 100644 --- a/src/previewsetup.ts +++ b/src/previewsetup.ts @@ -22,10 +22,10 @@ const isZipBytes = (buf: ArrayBuffer): boolean => { ((h[2] === 0x03 && h[3] === 0x04) || (h[2] === 0x05 && h[3] === 0x06)) } -const renderTextFallback = (el: HTMLElement, buf: ArrayBuffer, name?: string) => { +const renderTextFallback = (el: HTMLElement, buf: ArrayBuffer, name: string | undefined, kindLabel: string) => { const note = document.createElement('div') note.className = 'ui-fp-note' - note.textContent = `「${name ?? '该文件'}」不是有效的 docx 文件(缺少 ZIP 结构),已按文本内容展示;需要正式 Word 格式请让智能体以 docx 格式重新生成。` + note.textContent = `「${name ?? '该文件'}」不是有效的 ${kindLabel} 文件(缺少 ZIP 结构),已按文本内容展示;需要正式格式请让智能体重新生成。` const body = document.createElement('div') body.className = 'doc-viewer' body.innerHTML = marked.parse(new TextDecoder().decode(buf)) as string @@ -36,7 +36,7 @@ const renderTextFallback = (el: HTMLElement, buf: ArrayBuffer, name?: string) => const docxLoader = async (el: HTMLElement, source: { contentUrl?: string; name?: string }) => { const buf = await fetchBuf(source.contentUrl) if (!isZipBytes(buf)) { - renderTextFallback(el, buf, source.name) + renderTextFallback(el, buf, source.name, 'DOCX') return } const mammoth = await import('mammoth') @@ -44,9 +44,15 @@ const docxLoader = async (el: HTMLElement, source: { contentUrl?: string; name?: el.innerHTML = value } -const xlsxLoader = async (el: HTMLElement, source: { contentUrl?: string }) => { +const xlsxLoader = async (el: HTMLElement, source: { contentUrl?: string; name?: string }) => { + const buf = await fetchBuf(source.contentUrl) + // .xls 是 OLE 复合文档,不是 ZIP;只对 OOXML(xlsx/xlsm,或未给文件名)做魔数嗅探。 + if (!/\.xls$/i.test(source.name ?? '') && !isZipBytes(buf)) { + renderTextFallback(el, buf, source.name, 'XLSX') + return + } const XLSX = await import('xlsx') - const wb = XLSX.read(await fetchBuf(source.contentUrl), { type: 'array' }) + const wb = XLSX.read(buf, { type: 'array' }) el.innerHTML = '' const tabs = document.createElement('div') tabs.className = 'ui-fp-sheet-tabs' @@ -90,7 +96,13 @@ function renderSheet(wrap: HTMLElement, rows: unknown[][]) { wrap.append(table) } -const pptxLoader = async (el: HTMLElement, source: { contentUrl?: string }) => { +const pptxLoader = async (el: HTMLElement, source: { contentUrl?: string; name?: string }) => { + const buf = await fetchBuf(source.contentUrl) + // .ppt 是老版二进制;OOXML(pptx/ppsx)才要求 ZIP 魔数。 + if (!/\.ppt$/i.test(source.name ?? '') && !isZipBytes(buf)) { + renderTextFallback(el, buf, source.name, 'PPTX') + return + } // pptx2html@0.3.4 的 module 入口引用了大小写错配的 ./txml(Linux 打包必炸), // 按 D2 备选条款改用 pptx-preview(init(dom).preview(arrayBuffer) 全保真渲染)。 const { init } = await import('pptx-preview') @@ -99,7 +111,7 @@ const pptxLoader = async (el: HTMLElement, source: { contentUrl?: string }) => { stage.className = 'ui-fp-slide-stage' el.append(stage) const previewer = init(stage, { width: stage.clientWidth || 900 }) - await previewer.preview(await fetchBuf(source.contentUrl)) + await previewer.preview(buf) } export function wirePreviewCapabilities() {