Python 業務自動化 | ファイル・フォルダ自動化:基本操作 - 大容量ファイルコピー

Python Python
スポンサーリンク

大容量ファイルコピーは「安全に・確実に・途中で落ちにくく」行うことが最重要

数 GB〜数十 GB のファイルを扱うとき、
「普通にコピーするだけ」では、途中失敗・メモリ不足・時間が読めない、といった問題が出てきます。

Python で大容量ファイルをコピーするときに大事なのは、次のような視点です。

  • 一気に全部読み込まず、「少しずつ」コピーする
  • コピー中にエラーが起きても、異常終了しないようにする
  • コピーが本当に成功したか(サイズ・ハッシュ)を確認する

ここから、初心者向けに段階を踏んで解説していきます。


まずは標準的なコピー:shutil.copy2 を理解する

shutil.copy と shutil.copy2 の違い

大容量かどうかに関係なく、まずは標準のコピー関数を押さえます。

import shutil

src = "bigfile.dat"
dst = "backup/bigfile.dat"

shutil.copy2(src, dst)
Python

copy2 は「中身+メタ情報(更新日時など)」もコピーしてくれるので、
バックアップ用途では基本的に copy2 を使うと覚えておいて大丈夫です。

ただし、この書き方だと「内部的にどうコピーしているか」が見えません。
大容量ファイルで「進捗を出したい」「途中で止まったらどうする?」といった制御をしたいときは、
自分で「チャンク(小さい塊)ごとにコピーする」コードを書くのが定番です。


大容量ファイルを「チャンク単位」でコピーする基本

一気に読み込まず、少しずつコピーする理由

大きなファイルを read() で一気に読み込むと、
メモリを大量に消費して、最悪の場合メモリ不足で落ちます。

そこでよく使うのが「チャンク(塊)コピー」です。

def copy_large_file(src, dst, chunk_size=1024 * 1024):
    with open(src, "rb") as f_src, open(dst, "wb") as f_dst:
        while True:
            chunk = f_src.read(chunk_size)
            if not chunk:
                break
            f_dst.write(chunk)

copy_large_file("bigfile.dat", "backup/bigfile.dat")
Python

ここでのポイントは次の通りです。

  • "rb" / "wb" でバイナリモードにする(テキストではなく「生のバイト」として扱う)
  • chunk_size は 1MB(1024 * 1024)など、適度なサイズにする
  • read() が空(b"")を返したら終わり

このパターンを覚えると、「大きなものは全部チャンクで扱う」という感覚が身につきます。


進捗を表示しながらコピーするテンプレート

コピーがどこまで進んでいるかを知りたい場合

大容量コピーでは「あとどれくらい?」が分からないと不安になります。
ファイルサイズを事前に取得しておけば、進捗率を計算できます。

import os

def copy_large_file_with_progress(src, dst, chunk_size=1024 * 1024):
    total = os.path.getsize(src)
    copied = 0

    with open(src, "rb") as f_src, open(dst, "wb") as f_dst:
        while True:
            chunk = f_src.read(chunk_size)
            if not chunk:
                break

            f_dst.write(chunk)
            copied += len(chunk)

            progress = copied / total * 100
            print(f"\rコピー中... {progress:.2f}% 完了", end="")

    print("\nコピー完了")

copy_large_file_with_progress("bigfile.dat", "backup/bigfile.dat")
Python

ここでの深掘りポイントはこうです。

  • os.path.getsize(src) で総バイト数を取得
  • copied に書き込んだバイト数を足していく
  • copied / total * 100 で進捗率を計算
  • print(..., end="")\r で同じ行を上書き表示

これだけで「業務ツールっぽい」コピー処理になります。


コピー後に「本当に同じか」を確認する(ハッシュチェック)

サイズだけでは不安な場合のチェック方法

大容量コピーで怖いのは、「エラーにならずに終わったけど、中身が壊れていた」というパターンです。
これを防ぐために、ハッシュ(チェックサム) を使って元ファイルとコピー先が同じか確認できます。

import hashlib

def file_hash(path, chunk_size=1024 * 1024):
    h = hashlib.sha256()
    with open(path, "rb") as f:
        while True:
            chunk = f.read(chunk_size)
            if not chunk:
                break
            h.update(chunk)
    return h.hexdigest()

src = "bigfile.dat"
dst = "backup/bigfile.dat"

# ここでコピー処理を行ったと仮定

src_hash = file_hash(src)
dst_hash = file_hash(dst)

if src_hash == dst_hash:
    print("ハッシュ一致:コピー成功")
else:
    print("ハッシュ不一致:コピーに問題がある可能性があります")
Python

ポイントは次の通りです。

  • hashlib.sha256() でハッシュオブジェクトを作る
  • チャンクごとに update() していく(大容量でもOK)
  • 最後に hexdigest() で文字列として取得
  • 元と先のハッシュが一致すれば「中身も同じ」と判断できる

業務で「絶対に壊れてはいけないファイル」を扱うときは、
このハッシュチェックをセットで使うと安心感が段違いになります。


例題①:バックアップ前に空き容量を確認してから大容量コピーする

シナリオ

bigfile.dat をバックアップしたいが、空き容量が足りないと失敗する。
コピー前に「空き容量が十分か」をチェックしたい。

import os
import shutil

def has_enough_space(src, dst_dir):
    size = os.path.getsize(src)
    usage = shutil.disk_usage(dst_dir)
    return usage.free >= size

src = "bigfile.dat"
dst_dir = "backup"
dst = os.path.join(dst_dir, "bigfile.dat")

os.makedirs(dst_dir, exist_ok=True)

if not has_enough_space(src, dst_dir):
    print("空き容量不足のためコピーを中止します")
else:
    shutil.copy2(src, dst)
    print("コピー完了:", dst)
Python

ここでは、ディスク容量チェック+コピー を組み合わせています。
大容量ファイルでは「容量不足で途中失敗」が一番ダメージが大きいので、
事前チェックはかなり実務的です。


例題②:ネットワークドライブへのコピーをリトライ付きで行う

シナリオ

ネットワークドライブにコピーすると、たまに一時的なエラーで失敗する。
数回リトライして、それでもダメなら諦めたい。

import time
import shutil

def copy_with_retry(src, dst, retries=3, wait_sec=5):
    for i in range(1, retries + 1):
        try:
            shutil.copy2(src, dst)
            print("コピー成功:", dst)
            return True
        except Exception as e:
            print(f"{i} 回目のコピー失敗:", e)
            if i < retries:
                print(f"{wait_sec} 秒後にリトライします...")
                time.sleep(wait_sec)
    print("リトライ回数上限に達しました。コピー失敗。")
    return False

copy_with_retry("bigfile.dat", r"Z:\backup\bigfile.dat")
Python

ここでの深掘りポイントはこうです。

  • ネットワークは「一時的な失敗」が普通に起こる前提で設計する
  • try / except で失敗を捕まえ、time.sleep() で待ってから再試行
  • 「何回までリトライするか」を引数で調整できるようにしておく

大容量ファイルほど、途中失敗のダメージが大きいので、
リトライ設計はかなり効いてきます。


pathlib を使った、少しモダンで読みやすい大容量コピー

Path オブジェクトで書き直した例

from pathlib import Path

def copy_large_file_path(src: Path, dst: Path, chunk_size=1024 * 1024):
    dst.parent.mkdir(parents=True, exist_ok=True)

    with src.open("rb") as f_src, dst.open("wb") as f_dst:
        while True:
            chunk = f_src.read(chunk_size)
            if not chunk:
                break
            f_dst.write(chunk)

src = Path("bigfile.dat")
dst = Path("backup") / src.name

copy_large_file_path(src, dst)
print("コピー完了:", dst)
Python

pathlib を使うと、

  • パス結合が / で直感的
  • parent.mkdir() で親フォルダを簡単に作成
  • 型ヒント(Path)で「パスを扱っている」ことが明確

といったメリットがあり、業務コードが読みやすくなります。


大容量ファイルコピーを設計するときの考え方

大容量ファイルのコピーは、「ただ動けばいい」ではなく、
次のような観点で設計すると一気にプロっぽくなります。

  • 一気読みせず、チャンク単位でコピーする
  • 進捗を出して「どこまで進んでいるか」を見える化する
  • コピー前に空き容量を確認する
  • コピー後にサイズやハッシュで検証する
  • ネットワークコピーならリトライを前提にする

タイトルとURLをコピーしました