自動備份水井村USR資料夾——日期、日誌與ZIP封存
整合標準函式庫,建立可預覽、可驗證、可測試還原的地方資料備份流程。
完成本篇後,你能運用
datetime、pathlib、logging、zipfile、hashlib 與 json,建立時間戳備份檔、排除敏感資料、輸出清冊、驗證ZIP與SHA-256,並安全測試還原。一、壓縮檔不等於完整備份
把資料夾壓成ZIP只是備份流程的一步。真正可用的備份還要知道來源、建立時間、包含哪些檔案、是否完整、存放在哪裡,以及能否成功還原。
| 層次 | 問題 | 本篇做法 |
|---|---|---|
| 建立 | 是否遺漏或覆蓋? | 先預覽、時間戳命名、排他建立 |
| 驗證 | ZIP是否損壞? | testzip()與SHA-256 |
| 保存 | 是否只有同一顆硬碟一份? | 另存獨立媒體或受控儲存 |
| 還原 | 真的能取回資料嗎? | 定期還原到隔離測試目錄 |
二、使用時間戳建立唯一檔名
from datetime import datetime
def make_backup_name(project_name, now=None):
now = now or datetime.now()
timestamp = now.strftime("%Y%m%d_%H%M%S")
return f"{project_name}_{timestamp}.zip"
print(make_backup_name("shuijing_usr"))把 now 設成可傳入參數,測試時可固定時間,避免測試結果每秒不同。
三、先定義排除規則
from pathlib import Path
EXCLUDED_NAMES = {
".env",
"secrets.json",
"private_key.pem",
}
EXCLUDED_PARTS = {
"__pycache__",
".git",
"待授權原始媒體",
}
def should_exclude(path):
path = Path(path)
if path.name in EXCLUDED_NAMES:
return True
return any(part in EXCLUDED_PARTS for part in path.parts)排除清單須依專案實際資料政策共同確認,不能假設所有叫「照片」或「逐字稿」的內容都有相同公開權限。
四、建立備份預覽清單
from pathlib import Path
def collect_backup_files(source):
source = Path(source).resolve()
if not source.is_dir():
raise NotADirectoryError(f"來源不是資料夾:{source}")
files = []
for path in sorted(source.rglob("*")):
relative = path.relative_to(source)
if path.is_file() and not should_exclude(relative):
files.append((path, relative))
return files
def preview_backup(files):
total_size = sum(path.stat().st_size for path, _ in files)
print("預計檔案數:", len(files))
print("預計位元組數:", total_size)
for _, relative in files:
print("-", relative)五、建立SHA-256清冊
import hashlib
def sha256_file(path, chunk_size=1024 * 1024):
digest = hashlib.sha256()
with path.open("rb") as file:
while chunk := file.read(chunk_size):
digest.update(chunk)
return digest.hexdigest()
def build_manifest(files):
return [
{
"path": relative.as_posix(),
"size": source.stat().st_size,
"sha256": sha256_file(source),
}
for source, relative in files
]六、建立不覆蓋的ZIP備份
import json
import zipfile
from pathlib import Path
def create_backup(source, destination, confirmed=False):
if not confirmed:
raise PermissionError("尚未確認預覽清單")
source = Path(source).resolve()
destination = Path(destination).resolve()
files = collect_backup_files(source)
manifest = build_manifest(files)
destination.parent.mkdir(parents=True, exist_ok=True)
with destination.open("xb") as raw_file:
with zipfile.ZipFile(
raw_file, "w", compression=zipfile.ZIP_DEFLATED
) as archive:
for file_path, relative in files:
archive.write(file_path, arcname=relative.as_posix())
manifest_text = json.dumps(
manifest, ensure_ascii=False, indent=2
)
archive.writestr("backup_manifest.json", manifest_text)
return destinationxb 先取得排他建立的檔案物件,再交給 ZipFile,避免同名備份被覆蓋。ZIP內使用相對路徑,不保存來源電腦的絕對路徑。
七、記錄備份過程
import logging
from pathlib import Path
def configure_logging(log_path):
log_path = Path(log_path)
log_path.parent.mkdir(parents=True, exist_ok=True)
logging.basicConfig(
filename=log_path,
level=logging.INFO,
encoding="utf-8",
format="%(asctime)s | %(levelname)s | %(message)s",
force=True,
)
configure_logging("備份日誌/backup.log")
logging.info("備份流程開始")
logging.info("來源已完成預覽")日誌記錄時間、結果與錯誤即可,不應寫入Token、密碼、完整個資或不必要的敏感檔名。
八、驗證ZIP與備份檔雜湊
import zipfile
from pathlib import Path
def verify_backup(zip_path):
zip_path = Path(zip_path)
with zipfile.ZipFile(zip_path, "r") as archive:
bad_file = archive.testzip()
names = archive.namelist()
return {
"ZIP測試": "通過" if bad_file is None else f"損壞:{bad_file}",
"項目數": len(names),
"備份SHA256": sha256_file(zip_path),
}testzip() 會讀取ZIP成員並檢查CRC,但備份本身的SHA-256仍應另外保存,以便日後比對整個封存檔是否改變。
九、安全檢查ZIP成員路徑
還原外部或來源不明的ZIP前,須防範成員路徑逃出目標資料夾。以下先檢查,不直接解壓縮。
from pathlib import Path, PurePosixPath
import zipfile
def validate_archive_names(zip_path):
with zipfile.ZipFile(zip_path, "r") as archive:
for name in archive.namelist():
member = PurePosixPath(name)
if member.is_absolute() or ".." in member.parts:
raise ValueError(f"不安全的ZIP路徑:{name}")
if member.parts and ":" in member.parts[0]:
raise ValueError(f"疑似磁碟路徑:{name}")
return True十、還原到全新的隔離資料夾
from pathlib import Path
import zipfile
def restore_for_test(zip_path, target):
zip_path = Path(zip_path)
target = Path(target)
validate_archive_names(zip_path)
target.mkdir(parents=True, exist_ok=False)
target_root = target.resolve()
with zipfile.ZipFile(zip_path, "r") as archive:
for info in archive.infolist():
output = (target_root / info.filename).resolve()
if not output.is_relative_to(target_root):
raise ValueError(f"還原路徑越界:{info.filename}")
archive.extractall(target_root)
return target_root十一、比對清冊
import json
from pathlib import Path
def verify_restored_manifest(restored_folder):
restored_folder = Path(restored_folder)
manifest_path = restored_folder / "backup_manifest.json"
manifest = json.loads(
manifest_path.read_text(encoding="utf-8")
)
results = []
for item in manifest:
path = restored_folder / item["path"]
valid = (
path.is_file()
and path.stat().st_size == item["size"]
and sha256_file(path) == item["sha256"]
)
results.append({"path": item["path"], "valid": valid})
return results十二、整合主流程
from pathlib import Path
def main():
source = Path("shuijing_usr_demo")
backup_folder = Path("backup_output")
backup_name = make_backup_name("shuijing_usr")
destination = backup_folder / backup_name
files = collect_backup_files(source)
preview_backup(files)
answer = input("確認以上清單後輸入 YES:").strip()
if answer != "YES":
print("已取消,沒有建立備份。")
return
created = create_backup(
source, destination, confirmed=True
)
print("已建立:", created)
print(verify_backup(created))
if __name__ == "__main__":
main()互動確認只是教學防線,正式排程不可假裝有人確認;自動化前應完成固定來源、權限、排除規則、容量監控、通知及還原演練。
十三、3-2-1備份觀念
可採「至少3份資料、2種不同媒體、1份異地」作為基本思考,但仍須依組織政策、資料敏感度與法規調整。同步不是完整備份:誤刪、勒索軟體或錯誤同步也可能一起傳播。
十四、常見錯誤檢查表
| 現象 | 原因 | 修正 |
|---|---|---|
| ZIP建立了卻無法還原 | 未做還原演練 | 定期還原到隔離目錄並驗證清冊 |
| 備份含.env或私鑰 | 沒有排除與預覽 | 先建立規則並人工檢查清單 |
| 舊備份被蓋掉 | 固定檔名與覆寫模式 | 時間戳加xb排他建立 |
| 備份與原始資料一起故障 | 放在同一裝置 | 建立獨立媒體與異地副本 |
| 外部ZIP解壓後污染其他資料夾 | 未檢查成員路徑 | 驗證路徑並還原到新隔離目錄 |
十五、USR實作挑戰
建立含教材、CSV、照片及假密鑰檔的教學資料夾,確認預覽清單會排除敏感檔且不產生ZIP。
建立小型備份後記錄SHA-256,修改ZIP副本的一個位元組,觀察摘要與ZIP測試結果。
將備份還原到全新資料夾,逐筆比對清冊,寫出成功率、失敗項目及改善措施。
十六、與生成式AI協作
請擔任Python備份與USR資料治理助教。
請檢查我的資料夾ZIP備份程式:
1. 先預覽,確認後才建立;
2. 排除.env、密鑰、Token與未授權媒體;
3. 使用時間戳與排他建立,不覆蓋舊備份;
4. 建立檔案大小與SHA-256清冊;
5. 驗證ZIP並安全檢查成員路徑;
6. 只能還原到全新隔離資料夾;
7. 必須設計還原演練與結果日誌。
請先列出風險,再提供測試案例。備份的完成條件不是「看到ZIP檔」,而是內容經過預覽、封存後可驗證、被保存於獨立位置,並且確實通過還原演練。這正是把Python標準函式庫轉化為USR資料責任的實作。
沒有留言:
張貼留言