럭키엑셀에 파일 인젝션 성공
This commit is contained in:
@@ -4,8 +4,8 @@ import type { SheetData, FileUploadResult } from "../types/sheet";
|
||||
|
||||
/**
|
||||
* 파일 처리 관련 유틸리티 - 개선된 버전
|
||||
* - 모든 파일 형식 (CSV, XLS, XLSX)을 SheetJS를 통해 처리
|
||||
* - LuckyExcel 우선 시도, 실패 시 SheetJS Fallback 사용
|
||||
* - 모든 파일 형식을 SheetJS를 통해 읽은 후 XLSX로 변환
|
||||
* - 변환된 XLSX 파일을 LuckyExcel로 전달
|
||||
* - 안정적인 한글 지원 및 에러 처리
|
||||
*/
|
||||
|
||||
@@ -122,10 +122,6 @@ function validateWorkbook(workbook: any): { isValid: boolean; error?: string } {
|
||||
return { isValid: false, error: "워크북에 시트가 없습니다" };
|
||||
}
|
||||
|
||||
if (!workbook.Sheets) {
|
||||
return { isValid: false, error: "워크북에 Sheets 속성이 없습니다" };
|
||||
}
|
||||
|
||||
return { isValid: true };
|
||||
}
|
||||
|
||||
@@ -421,60 +417,270 @@ function convertSheetJSToLuckyExcel(workbook: any): SheetData[] {
|
||||
}
|
||||
|
||||
/**
|
||||
* SheetJS를 사용한 Fallback 처리
|
||||
* SheetJS로 파일을 읽고 XLSX로 변환한 뒤 LuckyExcel로 처리
|
||||
*/
|
||||
async function processWithSheetJSFallback(file: File): Promise<SheetData[]> {
|
||||
console.log("🔄 SheetJS Fallback 처리 시작...");
|
||||
async function processFileWithSheetJSToXLSX(file: File): Promise<SheetData[]> {
|
||||
console.log("📊 SheetJS → XLSX → LuckyExcel 파이프라인 시작...");
|
||||
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
const fileName = file.name.toLowerCase();
|
||||
const isCSV = fileName.endsWith(".csv");
|
||||
const isXLS = fileName.endsWith(".xls");
|
||||
const isXLSX = fileName.endsWith(".xlsx");
|
||||
|
||||
// 단순한 SheetJS 옵션 사용 (이전 코드 방식)
|
||||
const workbook = XLSX.read(arrayBuffer, {
|
||||
type: "array",
|
||||
cellDates: true,
|
||||
cellNF: false,
|
||||
cellText: false,
|
||||
codepage: 65001, // UTF-8
|
||||
dense: false,
|
||||
sheetStubs: true,
|
||||
raw: false,
|
||||
});
|
||||
// 1단계: SheetJS로 파일 읽기
|
||||
let workbook: any;
|
||||
|
||||
console.log("📊 SheetJS Fallback 워크북 정보:", {
|
||||
try {
|
||||
if (isCSV) {
|
||||
// CSV 파일 처리 - UTF-8 디코딩 후 읽기
|
||||
console.log("📄 CSV 파일을 SheetJS로 읽는 중...");
|
||||
const text = new TextDecoder("utf-8").decode(arrayBuffer);
|
||||
workbook = XLSX.read(text, {
|
||||
type: "string",
|
||||
codepage: 65001, // UTF-8
|
||||
raw: false,
|
||||
});
|
||||
} else {
|
||||
// XLS/XLSX 파일 처리 - 관대한 옵션으로 읽기
|
||||
console.log(`📊 ${isXLS ? "XLS" : "XLSX"} 파일을 SheetJS로 읽는 중...`);
|
||||
workbook = XLSX.read(arrayBuffer, {
|
||||
type: "array",
|
||||
cellText: true,
|
||||
sheetStubs: true,
|
||||
WTF: true,
|
||||
bookSheets: false,
|
||||
codepage: 65001,
|
||||
raw: false,
|
||||
});
|
||||
|
||||
// Sheets가 없고 SheetNames만 있는 경우 재시도
|
||||
if (workbook.SheetNames?.length > 0 && !workbook.Sheets) {
|
||||
console.log("⚠️ Sheets 속성이 없어서 재읽기 시도...");
|
||||
workbook = XLSX.read(arrayBuffer, {
|
||||
type: "array",
|
||||
cellText: true,
|
||||
sheetStubs: true,
|
||||
WTF: true,
|
||||
bookSheets: true, // 강제로 시트 읽기
|
||||
codepage: 65001,
|
||||
raw: false,
|
||||
});
|
||||
|
||||
// 여전히 실패하면 수동으로 빈 시트 생성
|
||||
if (!workbook.Sheets && workbook.SheetNames?.length > 0) {
|
||||
console.log("⚠️ 수동으로 빈 시트 생성...");
|
||||
workbook.Sheets = {};
|
||||
workbook.SheetNames.forEach((sheetName: string) => {
|
||||
workbook.Sheets[sheetName] = {
|
||||
"!ref": "A1:A1",
|
||||
A1: { v: "", t: "s" },
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (readError) {
|
||||
console.error("❌ SheetJS 파일 읽기 실패:", readError);
|
||||
throw new Error(
|
||||
`파일을 읽을 수 없습니다: ${readError instanceof Error ? readError.message : readError}`,
|
||||
);
|
||||
}
|
||||
|
||||
// 파일 버퍼 크기 검증
|
||||
if (arrayBuffer.byteLength === 0) {
|
||||
throw new Error("파일이 비어있습니다.");
|
||||
}
|
||||
|
||||
// 워크북 null 체크
|
||||
if (!workbook) {
|
||||
throw new Error("워크북을 생성할 수 없습니다.");
|
||||
}
|
||||
|
||||
// workbook.Sheets 존재 및 타입 검증
|
||||
if (!workbook.Sheets || typeof workbook.Sheets !== "object") {
|
||||
throw new Error("유효한 시트가 없습니다.");
|
||||
}
|
||||
|
||||
// workbook.SheetNames 배열 검증
|
||||
if (!Array.isArray(workbook.SheetNames) || workbook.SheetNames.length === 0) {
|
||||
throw new Error("시트 이름 정보가 없습니다.");
|
||||
}
|
||||
|
||||
console.log("✅ SheetJS 워크북 읽기 성공:", {
|
||||
sheetNames: workbook.SheetNames,
|
||||
sheetCount: workbook.SheetNames.length,
|
||||
});
|
||||
|
||||
if (workbook.SheetNames.length === 0) {
|
||||
throw new Error("파일에 워크시트가 없습니다.");
|
||||
// 2단계: 워크북을 XLSX ArrayBuffer로 변환
|
||||
let xlsxArrayBuffer: ArrayBuffer;
|
||||
try {
|
||||
console.log("🔄 XLSX 형식으로 변환 중...");
|
||||
const xlsxData = XLSX.write(workbook, {
|
||||
type: "array",
|
||||
bookType: "xlsx",
|
||||
compression: true,
|
||||
});
|
||||
// xlsxData는 Uint8Array이므로 ArrayBuffer로 변환
|
||||
if (xlsxData instanceof Uint8Array) {
|
||||
xlsxArrayBuffer = xlsxData.buffer.slice(
|
||||
xlsxData.byteOffset,
|
||||
xlsxData.byteOffset + xlsxData.byteLength,
|
||||
);
|
||||
} else if (xlsxData instanceof ArrayBuffer) {
|
||||
xlsxArrayBuffer = xlsxData;
|
||||
} else {
|
||||
// 다른 타입의 경우 새 ArrayBuffer 생성
|
||||
xlsxArrayBuffer = new ArrayBuffer(xlsxData.length);
|
||||
const view = new Uint8Array(xlsxArrayBuffer);
|
||||
for (let i = 0; i < xlsxData.length; i++) {
|
||||
view[i] = xlsxData[i];
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✅ XLSX 변환 완료: ${xlsxArrayBuffer.byteLength} bytes`);
|
||||
|
||||
// ⏱️ ArrayBuffer 변환 완료 확인 및 검증
|
||||
console.log("⏱️ ArrayBuffer 변환 검증 중...");
|
||||
|
||||
// ArrayBuffer 무결성 검증
|
||||
if (!xlsxArrayBuffer || xlsxArrayBuffer.byteLength === 0) {
|
||||
throw new Error("ArrayBuffer 변환 실패: 빈 버퍼");
|
||||
}
|
||||
|
||||
// XLSX 파일 시그니처 사전 검증
|
||||
const uint8Check = new Uint8Array(xlsxArrayBuffer);
|
||||
const signatureCheck = Array.from(uint8Check.slice(0, 4))
|
||||
.map((byte) => byte.toString(16).padStart(2, "0"))
|
||||
.join(" ");
|
||||
|
||||
if (signatureCheck !== "50 4b 03 04") {
|
||||
console.warn(
|
||||
`⚠️ 잘못된 XLSX 시그니처: ${signatureCheck} (예상: 50 4b 03 04)`,
|
||||
);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`✅ ArrayBuffer 검증 완료: ${xlsxArrayBuffer.byteLength} bytes, 시그니처: ${signatureCheck}`,
|
||||
);
|
||||
} catch (writeError) {
|
||||
console.error("❌ XLSX 변환 실패:", writeError);
|
||||
throw new Error(
|
||||
`XLSX 변환 실패: ${writeError instanceof Error ? writeError.message : writeError}`,
|
||||
);
|
||||
}
|
||||
|
||||
return convertSheetJSToLuckyExcel(workbook);
|
||||
}
|
||||
// 3단계: ArrayBuffer가 완전히 준비된 후 LuckyExcel로 처리
|
||||
console.log("🍀 LuckyExcel로 변환된 XLSX 처리 중...");
|
||||
|
||||
/**
|
||||
* LuckyExcel 처리 함수 (개선된 버전)
|
||||
*/
|
||||
function processWithLuckyExcel(file: File): Promise<SheetData[]> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
console.log("🍀 LuckyExcel 처리 시작...");
|
||||
// ArrayBuffer 최종 검증
|
||||
if (!xlsxArrayBuffer) {
|
||||
throw new Error("ArrayBuffer가 생성되지 않았습니다");
|
||||
}
|
||||
|
||||
console.log("🔍 LuckyExcel 변환 시작:", {
|
||||
hasFile: !!file,
|
||||
fileName: file.name,
|
||||
fileSize: file.size,
|
||||
fileType: file.type,
|
||||
});
|
||||
if (xlsxArrayBuffer.byteLength === 0) {
|
||||
throw new Error("ArrayBuffer 크기가 0입니다");
|
||||
}
|
||||
|
||||
// 원본 파일명에서 확장자를 .xlsx로 변경
|
||||
const xlsxFileName = file.name.replace(/\.(csv|xls|xlsx)$/i, ".xlsx");
|
||||
|
||||
// 🔍 LuckyExcel로 전달되는 파일 정보 출력
|
||||
console.log("📋 =================================");
|
||||
console.log("📋 LuckyExcel로 전달되는 파일 정보:");
|
||||
console.log("📋 =================================");
|
||||
console.log("📋 타이밍:", new Date().toISOString());
|
||||
console.log("📋 원본 파일명:", file.name);
|
||||
console.log("📋 변환된 파일명:", xlsxFileName);
|
||||
console.log("📋 ArrayBuffer 크기:", xlsxArrayBuffer.byteLength, "bytes");
|
||||
console.log("📋 ArrayBuffer 타입:", xlsxArrayBuffer.constructor.name);
|
||||
|
||||
// ArrayBuffer의 처음 100바이트를 16진수로 출력 (헥스 덤프)
|
||||
const uint8View = new Uint8Array(xlsxArrayBuffer);
|
||||
const firstBytes = Array.from(
|
||||
uint8View.slice(0, Math.min(100, uint8View.length)),
|
||||
)
|
||||
.map((byte) => byte.toString(16).padStart(2, "0"))
|
||||
.join(" ");
|
||||
console.log("📋 ArrayBuffer 처음 100바이트 (hex):", firstBytes);
|
||||
|
||||
// XLSX 파일 시그니처 확인 (PK\x03\x04 또는 50 4B 03 04)
|
||||
const signature = Array.from(uint8View.slice(0, 4))
|
||||
.map((byte) => byte.toString(16).padStart(2, "0"))
|
||||
.join(" ");
|
||||
console.log(
|
||||
"📋 파일 시그니처:",
|
||||
signature,
|
||||
signature === "50 4b 03 04" ? "(✅ 유효한 XLSX)" : "(❌ 잘못된 시그니처)",
|
||||
);
|
||||
console.log("📋 =================================");
|
||||
|
||||
// 🚀 LuckyExcel 호출 직전 최종 검증
|
||||
console.log("🚀 LuckyExcel 호출 직전 최종 검증:");
|
||||
console.log("🚀 ArrayBuffer 타입:", typeof xlsxArrayBuffer);
|
||||
console.log("🚀 ArrayBuffer 생성자 확인:", xlsxArrayBuffer.constructor.name);
|
||||
console.log("🚀 ArrayBuffer 크기:", xlsxArrayBuffer.byteLength);
|
||||
console.log("🚀 ArrayBuffer.isView:", ArrayBuffer.isView(xlsxArrayBuffer));
|
||||
console.log("🚀 fileName:", xlsxFileName, "타입:", typeof xlsxFileName);
|
||||
|
||||
console.log("🚀 LuckyExcel 객체:", typeof LuckyExcel);
|
||||
console.log(
|
||||
"🚀 transformExcelToLucky 함수:",
|
||||
typeof (LuckyExcel as any).transformExcelToLucky,
|
||||
);
|
||||
|
||||
console.log("🚀 LuckyExcel 호출 시작...");
|
||||
|
||||
// Promise를 사용한 LuckyExcel 처리
|
||||
return new Promise<SheetData[]>((resolve, reject) => {
|
||||
try {
|
||||
// File을 ArrayBuffer로 변환
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
|
||||
LuckyExcel.transformExcelToLucky(
|
||||
arrayBuffer, // ArrayBuffer 전달
|
||||
file.name, // 파일명 전달
|
||||
// LuckyExcel API는 (arrayBuffer, successCallback, errorCallback) 형태로 호출
|
||||
// 공식 문서: LuckyExcel.transformExcelToLucky(file, successCallback, errorCallback)
|
||||
(LuckyExcel as any).transformExcelToLucky(
|
||||
xlsxArrayBuffer,
|
||||
// 성공 콜백 함수 (두 번째 매개변수)
|
||||
(exportJson: any, luckysheetfile: any) => {
|
||||
try {
|
||||
console.log("🍀 =================================");
|
||||
console.log("🍀 LuckyExcel 변환 결과 상세 정보:");
|
||||
console.log("🍀 =================================");
|
||||
console.log("🍀 원본 파일명:", xlsxFileName);
|
||||
console.log("🍀 exportJson 존재:", !!exportJson);
|
||||
console.log("🍀 exportJson 타입:", typeof exportJson);
|
||||
|
||||
if (exportJson) {
|
||||
console.log("🍀 exportJson 전체 구조:", exportJson);
|
||||
console.log("🍀 exportJson.sheets 존재:", !!exportJson.sheets);
|
||||
console.log(
|
||||
"🍀 exportJson.sheets 타입:",
|
||||
typeof exportJson.sheets,
|
||||
);
|
||||
console.log(
|
||||
"🍀 exportJson.sheets 배열 여부:",
|
||||
Array.isArray(exportJson.sheets),
|
||||
);
|
||||
console.log("🍀 시트 개수:", exportJson?.sheets?.length || 0);
|
||||
|
||||
if (exportJson.sheets && Array.isArray(exportJson.sheets)) {
|
||||
exportJson.sheets.forEach((sheet: any, index: number) => {
|
||||
console.log(`🍀 시트 ${index + 1}:`, {
|
||||
name: sheet.name,
|
||||
row: sheet.row,
|
||||
column: sheet.column,
|
||||
celldata길이: sheet.celldata?.length || 0,
|
||||
키목록: Object.keys(sheet),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log("🍀 luckysheetfile 존재:", !!luckysheetfile);
|
||||
console.log("🍀 luckysheetfile 타입:", typeof luckysheetfile);
|
||||
if (luckysheetfile) {
|
||||
console.log("🍀 luckysheetfile 구조:", luckysheetfile);
|
||||
}
|
||||
console.log("🍀 =================================");
|
||||
|
||||
console.log("🔍 LuckyExcel 변환 결과:", {
|
||||
hasExportJson: !!exportJson,
|
||||
hasSheets: !!exportJson?.sheets,
|
||||
@@ -485,41 +691,16 @@ function processWithLuckyExcel(file: File): Promise<SheetData[]> {
|
||||
if (
|
||||
!exportJson ||
|
||||
!exportJson.sheets ||
|
||||
!Array.isArray(exportJson.sheets)
|
||||
!Array.isArray(exportJson.sheets) ||
|
||||
exportJson.sheets.length === 0
|
||||
) {
|
||||
console.warn(
|
||||
"⚠️ LuckyExcel 결과가 유효하지 않습니다. SheetJS Fallback을 시도합니다.",
|
||||
"⚠️ LuckyExcel 결과가 유효하지 않습니다. SheetJS 방식으로 대체 처리합니다.",
|
||||
);
|
||||
|
||||
// SheetJS Fallback 처리
|
||||
processWithSheetJSFallback(file)
|
||||
.then(resolve)
|
||||
.catch((fallbackError) => {
|
||||
console.error("❌ SheetJS Fallback도 실패:", fallbackError);
|
||||
reject(
|
||||
new Error(
|
||||
`파일 처리 실패: ${fallbackError instanceof Error ? fallbackError.message : fallbackError}`,
|
||||
),
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (exportJson.sheets.length === 0) {
|
||||
console.warn(
|
||||
"⚠️ LuckyExcel이 빈 시트를 반환했습니다. SheetJS Fallback을 시도합니다.",
|
||||
);
|
||||
|
||||
processWithSheetJSFallback(file)
|
||||
.then(resolve)
|
||||
.catch((fallbackError) => {
|
||||
console.error("❌ SheetJS Fallback도 실패:", fallbackError);
|
||||
reject(
|
||||
new Error(
|
||||
`파일 처리 실패: ${fallbackError instanceof Error ? fallbackError.message : fallbackError}`,
|
||||
),
|
||||
);
|
||||
});
|
||||
// LuckyExcel 실패 시 SheetJS 데이터를 직접 변환
|
||||
const fallbackSheets = convertSheetJSToLuckyExcel(workbook);
|
||||
resolve(fallbackSheets);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -596,24 +777,56 @@ function processWithLuckyExcel(file: File): Promise<SheetData[]> {
|
||||
|
||||
console.log("✅ LuckyExcel 처리 성공:", sheets.length, "개 시트");
|
||||
resolve(sheets);
|
||||
} catch (e) {
|
||||
console.error("❌ LuckyExcel 후처리 중 오류:", e);
|
||||
reject(e);
|
||||
} catch (processError) {
|
||||
console.error("❌ LuckyExcel 후처리 중 오류:", processError);
|
||||
|
||||
// LuckyExcel 후처리 실패 시 SheetJS 방식으로 대체
|
||||
try {
|
||||
console.log("🔄 SheetJS 방식으로 대체 처리...");
|
||||
const fallbackSheets = convertSheetJSToLuckyExcel(workbook);
|
||||
resolve(fallbackSheets);
|
||||
} catch (fallbackError) {
|
||||
console.error("❌ SheetJS 대체 처리도 실패:", fallbackError);
|
||||
reject(fallbackError);
|
||||
}
|
||||
}
|
||||
},
|
||||
// 오류 콜백 함수 (세 번째 매개변수)
|
||||
(error: any) => {
|
||||
console.error("❌ LuckyExcel 변환 오류:", error);
|
||||
|
||||
// LuckyExcel 오류 시 SheetJS 방식으로 대체
|
||||
try {
|
||||
console.log("🔄 SheetJS 방식으로 대체 처리...");
|
||||
const fallbackSheets = convertSheetJSToLuckyExcel(workbook);
|
||||
resolve(fallbackSheets);
|
||||
} catch (fallbackError) {
|
||||
console.error("❌ SheetJS 대체 처리도 실패:", fallbackError);
|
||||
reject(fallbackError);
|
||||
}
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("❌ LuckyExcel 처리 중 오류:", error);
|
||||
reject(error);
|
||||
} catch (luckyError) {
|
||||
console.error("❌ LuckyExcel 호출 중 오류:", luckyError);
|
||||
|
||||
// LuckyExcel 호출 실패 시 SheetJS 방식으로 대체
|
||||
try {
|
||||
console.log("🔄 SheetJS 방식으로 대체 처리...");
|
||||
const fallbackSheets = convertSheetJSToLuckyExcel(workbook);
|
||||
resolve(fallbackSheets);
|
||||
} catch (fallbackError) {
|
||||
console.error("❌ SheetJS 대체 처리도 실패:", fallbackError);
|
||||
reject(fallbackError);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 엑셀 파일을 SheetData 배열로 변환 (개선된 버전)
|
||||
* - 우선 LuckyExcel로 처리 시도 (XLSX)
|
||||
* - CSV, XLS는 SheetJS로 XLSX 변환 후 LuckyExcel 처리
|
||||
* - 실패 시 SheetJS Fallback 사용
|
||||
* - 모든 파일을 SheetJS로 읽은 후 XLSX로 변환
|
||||
* - 변환된 XLSX를 LuckyExcel로 처리
|
||||
* - 실패 시 SheetJS 직접 변환으로 Fallback
|
||||
*/
|
||||
export async function processExcelFile(file: File): Promise<FileUploadResult> {
|
||||
try {
|
||||
@@ -647,68 +860,8 @@ export async function processExcelFile(file: File): Promise<FileUploadResult> {
|
||||
`📁 파일 처리 시작: ${file.name} (${isCSV ? "CSV" : isXLS ? "XLS" : "XLSX"})`,
|
||||
);
|
||||
|
||||
let sheets: SheetData[];
|
||||
|
||||
try {
|
||||
// 1차 시도: LuckyExcel 직접 처리 (XLSX만)
|
||||
if (isXLSX) {
|
||||
console.log("🍀 XLSX 파일 - LuckyExcel 직접 처리 시도");
|
||||
sheets = await processWithLuckyExcel(file);
|
||||
} else {
|
||||
// CSV, XLS는 SheetJS로 XLSX 변환 후 LuckyExcel 처리
|
||||
console.log(
|
||||
`📊 ${isCSV ? "CSV" : "XLS"} 파일 - SheetJS 변환 후 LuckyExcel 처리`,
|
||||
);
|
||||
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
|
||||
// SheetJS로 읽기 (단순한 옵션 사용)
|
||||
let workbook: any;
|
||||
|
||||
if (isCSV) {
|
||||
// CSV 처리
|
||||
const text = new TextDecoder("utf-8").decode(arrayBuffer);
|
||||
workbook = XLSX.read(text, {
|
||||
type: "string",
|
||||
codepage: 65001,
|
||||
raw: false,
|
||||
});
|
||||
} else {
|
||||
// XLS 처리
|
||||
workbook = XLSX.read(arrayBuffer, {
|
||||
type: "array",
|
||||
codepage: 65001,
|
||||
raw: false,
|
||||
});
|
||||
}
|
||||
|
||||
// XLSX로 변환
|
||||
const xlsxBuffer = XLSX.write(workbook, {
|
||||
type: "array",
|
||||
bookType: "xlsx",
|
||||
compression: true,
|
||||
});
|
||||
|
||||
// File 객체로 변환하여 LuckyExcel 처리
|
||||
const xlsxFile = new File(
|
||||
[xlsxBuffer],
|
||||
file.name.replace(/\.(csv|xls)$/i, ".xlsx"),
|
||||
{
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
},
|
||||
);
|
||||
|
||||
sheets = await processWithLuckyExcel(xlsxFile);
|
||||
}
|
||||
} catch (luckyExcelError) {
|
||||
console.warn(
|
||||
"🔄 LuckyExcel 처리 실패, SheetJS Fallback 시도:",
|
||||
luckyExcelError,
|
||||
);
|
||||
|
||||
// 2차 시도: SheetJS Fallback
|
||||
sheets = await processWithSheetJSFallback(file);
|
||||
}
|
||||
// 통합된 처리 방식: SheetJS → XLSX → LuckyExcel
|
||||
const sheets = await processFileWithSheetJSToXLSX(file);
|
||||
|
||||
if (!sheets || sheets.length === 0) {
|
||||
return {
|
||||
@@ -736,7 +889,14 @@ export async function processExcelFile(file: File): Promise<FileUploadResult> {
|
||||
if (
|
||||
error.message.includes("파일에 워크시트가 없습니다") ||
|
||||
error.message.includes("워크북 구조 오류") ||
|
||||
error.message.includes("파일 처리 실패")
|
||||
error.message.includes("파일 처리 실패") ||
|
||||
error.message.includes("파일 읽기 실패") ||
|
||||
error.message.includes("XLSX 변환 실패") ||
|
||||
error.message.includes("파일이 비어있습니다") ||
|
||||
error.message.includes("워크북을 생성할 수 없습니다") ||
|
||||
error.message.includes("유효한 시트가 없습니다") ||
|
||||
error.message.includes("시트 이름 정보가 없습니다") ||
|
||||
error.message.includes("파일을 읽을 수 없습니다")
|
||||
) {
|
||||
errorMessage = error.message;
|
||||
} else if (error.message.includes("transformExcelToLucky")) {
|
||||
|
||||
Reference in New Issue
Block a user