/* global Office, console */ /** * ดึงไฟล์ .docx ปัจจุบันผ่าน File/Slice API (FR-2 ขั้นตอนที่ 5) * กติกา: Word เปิด File object พร้อมกันได้แค่ 2 ตัว → เรียก closeAsync เสมอใน finally */ export const DOCX_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; /** ขนาด slice ต่อครั้ง — 1MB กันกิน memory ยักษ์ในเครื่องรายงานน้อย */ const SLICE_SIZE = 1024 * 1024; function officeError(result: Office.AsyncResult, fallback: string): Error { return new Error(result.error?.message || fallback); } function getFileAsync(): Promise { return new Promise((resolve, reject) => { try { Office.context.document.getFileAsync( Office.FileType.Compressed, { sliceSize: SLICE_SIZE }, (result) => { if (result.status === Office.AsyncResultStatus.Succeeded) { resolve(result.value); } else { reject(officeError(result, "เปิดไฟล์เอกสารไม่สำเร็จ")); } } ); } catch (error) { reject(error instanceof Error ? error : new Error("เปิดไฟล์เอกสารไม่สำเร็จ")); } }); } /** host ส่ง slice มาเป็น ArrayBuffer หรือ view — รับทั้งคู่แล้วคืนเป็น BlobPart ตรงๆ */ function getSliceAsync(file: Office.File, sliceIndex: number): Promise { return new Promise((resolve, reject) => { try { file.getSliceAsync(sliceIndex, (result) => { if (result.status === Office.AsyncResultStatus.Succeeded) { const data: unknown = result.value.data; if (data instanceof ArrayBuffer) { resolve(data); } else if (ArrayBuffer.isView(data)) { const view = data as ArrayBufferView; resolve(new Uint8Array(view.buffer as ArrayBuffer, view.byteOffset, view.byteLength)); } else { reject(new Error("รูปแบบข้อมูลของ slice ไม่รองรับ (ต้องเป็น byte array)")); } } else { reject(officeError(result, `อ่าน slice ที่ ${sliceIndex} ไม่สำเร็จ`)); } }); } catch (error) { reject(error instanceof Error ? error : new Error(`อ่าน slice ที่ ${sliceIndex} ไม่สำเร็จ`)); } }); } /** ปิด File object — fail ก็แค่ warn ไม่ทับ error ของ flow หลัก */ function closeFileQuietly(file: Office.File): Promise { return new Promise((resolve) => { try { file.closeAsync(() => resolve()); } catch (error) { console.warn("closeAsync failed:", error); resolve(); } }); } /** * ประกอบไฟล์ทั้งไฟล์เป็น Blob — อ่าน slice ทีละชิ้นตามลำดับ * เรียก closeAsync เสมอ (สำเร็จหรือ fail ก็ตาม) */ export async function getCurrentDocumentFile(): Promise { const file = await getFileAsync(); try { const chunks: BlobPart[] = []; for (let index = 0; index < file.sliceCount; index++) { chunks.push(await getSliceAsync(file, index)); } return new Blob(chunks, { type: DOCX_CONTENT_TYPE }); } finally { await closeFileQuietly(file); } }