aiaiteam Claude Sonnet 4.6 commited on
Commit
cc24801
·
1 Parent(s): 9d67707

fix: HWP 추출 오류 진단 강화 + openstream 문자열 경로로 변경

Browse files

- _ext_hwp: list 대신 문자열 경로 사용 (BodyText/SectionN)
- _ext_hwp: OLE 항목 대소문자 무시 비교
- _ext_hwp: 각 단계별 명시적 오류 메시지 + print 로그
- _ext_hwp: zlib 실패 시 일반 zlib 재시도 후 raw 폴백
- /extract: str(e) 빈 문자열이면 type name 대신 사용

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (1) hide show
  1. app.py +42 -18
app.py CHANGED
@@ -290,32 +290,55 @@ def _ext_hwp(path: Path) -> str:
290
  try:
291
  import olefile
292
  except ImportError:
293
- raise RuntimeError("olefile 라이브러리 없음 (pip install olefile)")
294
 
295
- ole = olefile.OleFileIO(str(path))
 
 
 
296
 
297
  compressed = True
298
- if ole.exists("FileHeader"):
299
- hdr = ole.openstream("FileHeader").read()
300
- if len(hdr) >= 40:
301
- compressed = bool(struct.unpack_from("<I", hdr, 36)[0] & 0x1)
302
-
303
- # Discover section names dynamically — handles Section0, Section0001, etc.
304
- sections = sorted(
305
- (e[1] for e in ole.listdir()
306
- if len(e) == 2 and e[0] == "BodyText" and e[1].startswith("Section")),
307
- key=lambda s: int(re.sub(r"\D", "", s) or 0),
308
- )
 
 
 
 
 
 
 
 
 
 
 
309
  if not sections:
310
- raise RuntimeError("BodyText 섹션을 찾을 없습니다. 지원되지 않는 HWP 버전일 수 있습니다.")
 
311
 
312
  lines: list[str] = []
313
  for sec_name in sections:
314
- raw = ole.openstream(["BodyText", sec_name]).read()
 
 
 
 
 
315
  try:
316
  data = zlib.decompress(raw, -15) if compressed else raw
317
  except zlib.error:
318
- data = raw
 
 
 
319
 
320
  pos = 0
321
  while pos + 4 <= len(data):
@@ -337,7 +360,7 @@ def _ext_hwp(path: Path) -> str:
337
  if cp == 0x0D:
338
  lines.append("".join(chars)); chars = []
339
  elif 0x01 <= cp <= 0x0C:
340
- t += 8 # inline object: skip 8-byte attribute block
341
  elif cp >= 0x20:
342
  chars.append(chr(cp))
343
  if chars:
@@ -428,7 +451,8 @@ async def extract_file(file: UploadFile = File(...)):
428
  else:
429
  text = _ext_txt(tmp_path)
430
  except Exception as e:
431
- raise HTTPException(500, str(e))
 
432
  finally:
433
  tmp_path.unlink(missing_ok=True)
434
 
 
290
  try:
291
  import olefile
292
  except ImportError:
293
+ raise RuntimeError("olefile 미설치 (pip install olefile)")
294
 
295
+ try:
296
+ ole = olefile.OleFileIO(str(path))
297
+ except Exception as exc:
298
+ raise RuntimeError(f"HWP OLE 열기 실패 [{type(exc).__name__}]: {exc or 'no detail'}")
299
 
300
  compressed = True
301
+ try:
302
+ if ole.exists("FileHeader"):
303
+ hdr = ole.openstream("FileHeader").read()
304
+ if len(hdr) >= 40:
305
+ compressed = bool(struct.unpack_from("<I", hdr, 36)[0] & 0x1)
306
+ except Exception:
307
+ pass
308
+
309
+ # Discover sections: case-insensitive, any naming convention
310
+ try:
311
+ all_entries = ole.listdir()
312
+ print(f"[HWP] OLE entries: {all_entries[:20]}", flush=True)
313
+ sections = sorted(
314
+ (e[1] for e in all_entries
315
+ if isinstance(e, (list, tuple)) and len(e) >= 2
316
+ and str(e[0]).lower() == "bodytext"
317
+ and str(e[1]).lower().startswith("section")),
318
+ key=lambda s: int(re.sub(r"\D", "", s) or "0"),
319
+ )
320
+ except Exception as exc:
321
+ raise RuntimeError(f"섹션 목록 오류 [{type(exc).__name__}]: {exc or 'no detail'}")
322
+
323
  if not sections:
324
+ entry_dump = str([str(e) for e in all_entries[:20]])
325
+ raise RuntimeError(f"BodyText/Section* 없음. OLE 항목: {entry_dump}")
326
 
327
  lines: list[str] = []
328
  for sec_name in sections:
329
+ try:
330
+ raw = ole.openstream(f"BodyText/{sec_name}").read()
331
+ except Exception as exc:
332
+ print(f"[HWP] openstream {sec_name} 오류: {exc}", flush=True)
333
+ continue
334
+
335
  try:
336
  data = zlib.decompress(raw, -15) if compressed else raw
337
  except zlib.error:
338
+ try:
339
+ data = zlib.decompress(raw)
340
+ except zlib.error:
341
+ data = raw
342
 
343
  pos = 0
344
  while pos + 4 <= len(data):
 
360
  if cp == 0x0D:
361
  lines.append("".join(chars)); chars = []
362
  elif 0x01 <= cp <= 0x0C:
363
+ t += 8
364
  elif cp >= 0x20:
365
  chars.append(chr(cp))
366
  if chars:
 
451
  else:
452
  text = _ext_txt(tmp_path)
453
  except Exception as e:
454
+ detail = str(e).strip() or type(e).__name__
455
+ raise HTTPException(500, detail)
456
  finally:
457
  tmp_path.unlink(missing_ok=True)
458