フォルダと拡張子を指定してフォルダ内のファイルを別の1つのフォルダにコピー


はじめに

サーバーに存在するサブフォルダを含む大量のファイルをローカルの1つのフォルダにダウンロード(コピー)して処理したい時が割とある。
フォルダごとに同一名のファイルが存在する場合はコピーする際に上書きされる可能性がある。
また、フォルダのデータ全てをコピーするのではなく、特定の拡張子のファイルだけコピーしたい場合が結構ある。
そんな課題を解決するためのGUIを作成した。

仕様

・上下2つの「フォルダを選択」ボタンで「コピー元フォルダ(1行目)」と「コピー先フォルダ(2行目)」を選択する。
・3行目にコピー対象の拡張子を「.」抜きで入力する※最大5つ
・「実行ボタン」でコピー処理が実行される
・コピー先のファイル名はコピー元のディレクトリを起点とした相対パスのパス文字がアンダーバーに変換された名前が付けられる

コード

import PySimpleGUI as sg
import os
import re, shutil
import pathlib

INPUT_BOX_SIZE = 5
HEADER_ROW = 1
INI_EXT_LINE = 2
MAX_NUM_EXT = 5

#### 関数
# ファイル名変換関数
def name_savefile_dirname(path ,org_dir):
    file_path = path.replace('/', os.sep) #パスの区切り文字を統一
    org_path = pathlib.Path(org_dir)
    sub_path = pathlib.Path(file_path)
    file_name = sub_path.relative_to(org_path) # 指定ディレクトリ基準のファイルの相対パスを取得
    return str(file_name).replace("\\","_") #パスの区切り文字をアンダーバーに変換

# ファイルコピー関数
def copy_files(org_dir, save_dir, target_ext):
    file_list = [] #コピーするファイルのリスト
    filepath_list = [] #コピーするファイルパスのリスト
    for dirpath, dirnames, filenames in os.walk(org_dir): #コピー元のディレクトリを基準にtarget_extで指定した拡張子のファイルをリスト化
        for file in filenames:
            for ext in target_ext:
                m = re.search(re.escape(ext), file) #指定した拡張子のファイルを探す
                if m :
                    file_list.append(file) #ファイル名をリストに追加
                    filepath_list.append(os.path.join(dirpath, file)) #コピー元ファイルのパスをリストに追加

    for i in range(len(file_list)):
        save_file_path = os.path.join(save_dir, name_savefile_dirname(filepath_list[i], org_dir)) #コピー先ディレクトリ名とコピー元ディレクトリを付けたファイル名を結合
        shutil.copy2(filepath_list[i], save_file_path) #ファイルをコピー
        print(save_file_path)

#### GUI
#  セクション1 - オプションの設定と標準レイアウト
sg.theme('Dark Blue 3')

layout = [
    #1行目
    [sg.InputText('コピー元フォルダ',enable_events=True,), sg.FolderBrowse('フォルダを選択', key='folder_from')],
    #2行目
    [sg.InputText('コピー先フォルダ',enable_events=True,), sg.FolderBrowse('フォルダを選択', key='folder_to')],
    #3行目
    [sg.Text('拡張子(ex:csv)', size=(15, 1)),sg.InputText('csv', size=(INPUT_BOX_SIZE, 1)),
     sg.InputText('xlsx', size=(INPUT_BOX_SIZE, 1)),sg.InputText('', size=(INPUT_BOX_SIZE, 1)),
     sg.InputText('', size=(INPUT_BOX_SIZE, 1)), sg.InputText('', size=(INPUT_BOX_SIZE, 1))],
    #4行目
    [sg.Submit(button_text='実行ボタン')]
]

# セクション 2 - ウィンドウの生成
window = sg.Window('ファイルコピー', layout)

# セクション 3 - イベントループ
while True:
    event, values = window.read()

    if event is None:
        print('exit')
        break

    if event == '実行ボタン':
        # フォルダのパスを抽出
        path_from = values['folder_from']
        path_to = values['folder_to']

        # 入力がある場合コピー対象とするように拡張子をリスト化
        ext_list =[]
        for i in range(MAX_NUM_EXT):
            if not len(values[INI_EXT_LINE+i].strip())==0:
                ext_list.append("." + values[INI_EXT_LINE+i].strip())

        # コピー関数実行
        copy_files(path_from, path_to, ext_list)

        # ポップアップ
        sg.popup("終了~")

# セクション 4 - ウィンドウの破棄と終了
window.close()