fix: BIP→BSQ 转换数据丢失修复 + 只读文件处理

- 延迟删除原始文件:先验证临时 BSQ 文件存在且非空,再删原始数据
- 异常处理不再删除临时 BSQ:保留完好副本供手动恢复
- 新增 _make_writable() 移除只读属性,避免 Windows PermissionError
- 清理孤儿 .aux.xml 临时文件
- 路径变量提前声明,保证异常处理块可访问
This commit is contained in:
duxin
2026-07-28 14:59:03 +08:00
parent c16cddf384
commit 46c5e51772

View File

@ -98,6 +98,23 @@ def _ensure_bsq(img_path):
f"{_src_size/1024**3:.1f}GB, 写入临时文件后原地替换)") f"{_src_size/1024**3:.1f}GB, 写入临时文件后原地替换)")
import time as _t import time as _t
_t0 = _t.time() _t0 = _t.time()
# 路径变量提前声明,确保异常处理中可以访问
_tmp_data = str(_tmp_bsq)
_tmp_hdr = _tmp_data + '.hdr'
_tmp_aux = _tmp_data + '.aux.xml'
_bip_hdr = img_path + '.hdr'
if not os.path.exists(_bip_hdr):
_bip_hdr = os.path.splitext(img_path)[0] + '.hdr'
def _make_writable(filepath):
"""去掉只读属性,避免 Windows 下 unlink/rename 拒绝访问"""
if os.path.exists(filepath):
try:
os.chmod(filepath, 0o666)
except Exception:
pass
try: try:
from osgeo import gdal from osgeo import gdal
gdal.UseExceptions() gdal.UseExceptions()
@ -108,14 +125,11 @@ def _ensure_bsq(img_path):
) )
_elapsed = _t.time() - _t0 _elapsed = _t.time() - _t0
# 记录临时文件路径(后续重命名时用到) # ★ 验证临时 BSQ 文件是否成功生成
_tmp_data = str(_tmp_bsq) if not os.path.exists(_tmp_data) or os.path.getsize(_tmp_data) == 0:
_tmp_hdr = _tmp_data + '.hdr' raise RuntimeError(f"gdal.Translate 未生成有效的临时 BSQ 文件: {_tmp_data}")
# ★ 保存原始 HDR 中的波长/元数据gdal.Translate 不保留) # ★ 保存原始 HDR 中的波长/元数据gdal.Translate 不保留)
_bip_hdr = img_path + '.hdr'
if not os.path.exists(_bip_hdr):
_bip_hdr = os.path.splitext(img_path)[0] + '.hdr'
_hdr_extras = {} # 需要保留的元数据 _hdr_extras = {} # 需要保留的元数据
if os.path.exists(_bip_hdr): if os.path.exists(_bip_hdr):
try: try:
@ -142,22 +156,36 @@ def _ensure_bsq(img_path):
except Exception: except Exception:
pass pass
# 删除原始 BIP 及其 HDR # ★ 先处理只读属性,再删除原始 BIP 及其 HDR
if not os.path.exists(_bip_hdr): _make_writable(str(_src))
_bip_hdr = os.path.splitext(img_path)[0] + '.hdr' _make_writable(_bip_hdr)
_src.unlink() _src.unlink()
if os.path.exists(_bip_hdr): if os.path.exists(_bip_hdr):
os.unlink(_bip_hdr) os.unlink(_bip_hdr)
# 将 BSQ 临时文件重命名为原始文件名(所有下游步骤零改动) # 将 BSQ 临时文件重命名为原始文件名(所有下游步骤零改动)
_final = str(_src) _final = str(_src)
os.rename(_tmp_data, _final)
_final_hdr = _final + '.hdr' _final_hdr = _final + '.hdr'
# 如果目标 HDR 残留(只读等情况),先处理再删除
_make_writable(_final_hdr)
if os.path.exists(_final_hdr):
os.unlink(_final_hdr)
os.rename(_tmp_data, _final)
if os.path.exists(_tmp_hdr): if os.path.exists(_tmp_hdr):
if os.path.exists(_final_hdr):
os.unlink(_final_hdr)
os.rename(_tmp_hdr, _final_hdr) os.rename(_tmp_hdr, _final_hdr)
# ★ 清理孤儿 aux.xml临时文件和可能残留的目标 aux.xml
_final_aux = _final + '.aux.xml'
for _orphan_aux in [_tmp_aux, _final_aux]:
if os.path.exists(_orphan_aux):
try:
_make_writable(_orphan_aux)
os.unlink(_orphan_aux)
except Exception:
pass
# ★ 将波长等元数据写回新 HDR # ★ 将波长等元数据写回新 HDR
if _hdr_extras and os.path.exists(_final_hdr): if _hdr_extras and os.path.exists(_final_hdr):
try: try:
@ -179,14 +207,20 @@ def _ensure_bsq(img_path):
f"BIP → BSQ 原地替换,所有路径无需修改") f"BIP → BSQ 原地替换,所有路径无需修改")
return str(_final) return str(_final)
except Exception as e: except Exception as e:
# 清理失败的临时文件 # ★ 异常处理:绝不删除临时 BSQ 文件(它可能是唯一完好的数据副本)
if os.path.exists(_tmp_data): # 如果 gdal.Translate 成功生成了临时文件,保留它供手动恢复
try: if os.path.exists(_tmp_data) and os.path.getsize(_tmp_data) > 0:
os.unlink(_tmp_data) print(f"[BIP→BSQ] ⚠ 转换中途失败,但临时 BSQ 文件已保留: {_tmp_data}")
if os.path.exists(_tmp_hdr): print(f"[BIP→BSQ] 请手动检查该文件,确认无误后可重命名为 {img_path}")
os.unlink(_tmp_hdr) else:
except Exception: # 只有转换根本没产出时才清理(可能是磁盘满等)
pass for _f in [_tmp_data, _tmp_hdr, _tmp_aux]:
if os.path.exists(_f):
try:
_make_writable(_f)
os.unlink(_f)
except Exception:
pass
print(f"[BIP→BSQ] 转换失败: {e},继续使用 BIP") print(f"[BIP→BSQ] 转换失败: {e},继续使用 BIP")
return img_path return img_path