implement phase 4
This commit is contained in:
parent
25c2ce53cd
commit
f4b1c3e14b
27 changed files with 6929 additions and 48 deletions
90
addin/src/services/fileService.ts
Normal file
90
addin/src/services/fileService.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/* 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<unknown>, fallback: string): Error {
|
||||
return new Error(result.error?.message || fallback);
|
||||
}
|
||||
|
||||
function getFileAsync(): Promise<Office.File> {
|
||||
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<BlobPart> {
|
||||
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<void> {
|
||||
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<Blob> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue