こんにちは デジタル事業本部の藤田です。
弊社ではDirectCloudというオンラインストレージサービスを活用しているのですが、このDirectCloudでクライアントとファイルをやり取りをすることも多くあります。
オンラインストレージでのファイルやり取りは便利なのですが、数が多くなるとなかなか大変です。
そんな中、以下のようなやり取りをする案件があるとのことで相談されました。
- クライアントさんが多数(100以上)ある
- ストレージ上にはクライアントの数だけディレクトリがある
- アップする前にディレクトリの中をひとつのディレクトリにまとめる
- それぞれのクライアントごとにそれぞれアップするファイルがある
- 数が多いのでこれらを手作業で繰り返すのはつらい…
という案件についてプログラムで効率化できましたので、備忘録も兼ねて記載しておきます。
目次
DirectCloudにはAPIがある。が、しかし!
まず最初に思いついたのはDirectCloudのAPI(Application Programming Interface)連携です。
DirectCloudにはAPIが用意されており、プログラムを組めば一括でファイルのアップロードぐらい楽勝だろう… と思っていたのですが、社内の事情でAPIが使えないプランと契約していたことが判明。目論んでいた手法が使えないことで退路を断たれいきなり途方に暮れてしまいました。
DirectCloudドライブを使ってみる
しかし、APIが使えないプランであってもDirectCloudドライブなる機能があることが判明。一筋の光が見えました。
DirectCloudドライブとは
エクスプローラーやFinderでクラウド上のファイルにアクセスし、編集・保存・移動・コピー・削除などのファイル操作ができるので安全かつ便利です。
参照:PCにデータを残さずセキュアにファイル共有・編集
Windowsでいうエクスプローラー上でDirectCloudのディレクトリがアクセスできるようになる機能です。こちらはプランによる制限はありませんので使用できました。また、以前はWindows用のみでしたが2022年2月よりmacOS用(β版)がリリースされています。
このDirectCloudドライブであればOS上でのファイル操作でアップロードができそうです。
大量ファイルをアップロードする流れ
今回はファイルをアップロードするためのフローを2回に分けました。
1.アップする前にディレクトリの中をひとつのディレクトリにまとめる
まず、ディレクトリの中のファイル/フォルダをひとつのディレクトリにまとめる作業があります。
2.それぞれのクライアントごとにそれぞれアップするファイルがある
そして、ローカルからストレージ上の各ディレクトリへファイルをアップロードしていきます。
ファイル処理をおこなうプログラム
上記の操作(ディレクトリの中をひとつのディレクトリにまとめる処理およびディレクトリにファイルをアップロードする処理)をいろいろな言語で書いてみました。いずれもサンプルのコードなので1つのディレクトリを操作するだけとしていますが、実際はこのコードをループさせています。
AppleScript
use AppleScript version "2.4" use framework "Foundation" use scripting additions property NSArray : a reference to current application's NSArray property NSString : a reference to current application's NSString property NSPredicate : a reference to current application's NSPredicate property NSFileManager : a reference to current application's NSFileManager -- フォルダ内に作成するフォルダの名前 property putFolderName : "001" -- 移動元フォルダ set pathA to POSIX path of (choose folder with prompt ("移動元フォルダを選択")) -- 移動元フォルダのパス set aPath to NSString's stringWithString:pathA -- 移動先フォルダのパス set aPath_ to aPath's stringByAppendingPathComponent:(NSString's stringWithString:putFolderName) -- コピー元 set pathB to POSIX path of (choose folder with prompt ("コピー元フォルダを選択")) -- コピー元フォルダのパス set bPath to NSString's stringWithString:pathB -- NSFileManagerを取得 set aManager to NSFileManager's defaultManager() -- 移動元フォルダの中身をリストアップ set aItems to aManager's contentsOfDirectoryAtPath:pathA |error|:(missing value) -- コピー元フォルダの中身をリストアップ set bItems to aManager's contentsOfDirectoryAtPath:pathB |error|:(missing value) -- フォルダ「001」があるかどうか if aManager's fileExistsAtPath:aPath_ isDirectory:true then -- フォルダ「001」を省く set aPredicate to NSPredicate's predicateWithFormat_("self != %@", putFolderName) set aItems to aItems's filteredArrayUsingPredicate:aPredicate end if -- フォルダを作成 set aRes to aManager's createDirectoryAtPath:aPath_ withIntermediateDirectories:true attributes:(missing value) |error|:(reference) -- 移動元フォルダの第一階層パス set aItemArray to aPath's stringsByAppendingPaths:aItems -- 移動元から移動 repeat with aItem in aItemArray set aPath2 to (aPath_'s stringByAppendingPathComponent:(aItem's lastPathComponent())) -- アイテムを移動 set moveRes to (aManager's moveItemAtPath:aItem toPath:aPath2 |error|:(missing value)) end repeat -- コピー元フォルダの第一階層パス set bItemArray to bPath's stringsByAppendingPaths:bItems -- コピー元からコピー repeat with bItem in bItemArray set bPath2 to (aPath's stringByAppendingPathComponent:(bItem's lastPathComponent())) -- アイテムをコピー set copyRes to (aManager's copyItemAtPath:bItem toPath:bPath2 |error|:(missing value)) end repeat return true
上記のコードはAppleScriptObjCです。実はノーマルなAppleScriptでもファイル移動やコピーはできる(しかもコードは短い)のですが、大量にファイルやフォルダがあると処理速度がちがいます。また、ファイルもフォルダも区別せずコピーできるのはコードを書くには楽です。
こちらはAppleScriptなのでmacOSでのみ動作します。
Excel VBA
Sub CopyItemAtPath() '変数を宣言する Dim ps, PutFolderName, PathA, PathA_, PathB As String 'パスセレクター:通常は\ ps = Application.PathSeparator 'フォルダ名 PutFolderName = "001" '移動元フォルダ With Application.FileDialog(msoFileDialogFolderPicker) .Title = "移動元フォルダを選択" '選択したフォルダ If .Show = True Then PathA = .SelectedItems(1) + ps Else Exit Sub End If End With 'コピー元フォルダ With Application.FileDialog(msoFileDialogFolderPicker) .Title = "コピー元フォルダを選択" '選択したフォルダ If .Show = True Then PathB = .SelectedItems(1) + ps Else Exit Sub End If End With 'インスタンス Set Fso = CreateObject("Scripting.FileSystemObject") 'フォルダをオブジェクト化 Set FolderObjA = Fso.GetFolder(PathA) 'フォルダのパス PathA_ = PathA + PutFolderName + ps 'フォルダがなければ If Not Fso.FolderExists(PathA_) Then 'フォルダを作成 Fso.CreateFolder PathA_ End If 'フォルダ内のファイル For Each FileA In FolderObjA.Files 'ファイルを移動 Fso.MoveFile FileA.Path, PathA_ Next 'フォルダ内のサブフォルダ For Each FolderA In FolderObjA.subFolders '移動先フォルダではないかどうか If FolderA.Name <> PutFolderName Then 'サブフォルダを移動 Fso.MoveFolder FolderA.Path, PathA_ End If Next 'フォルダをオブジェクト化 Set FolderObjB = Fso.GetFolder(PathB) 'フォルダ内のファイル For Each FileB In FolderObjB.Files 'ファイルをコピー Fso.CopyFile FileB.Path, PathA Next 'フォルダ内のサブフォルダ For Each FolderB In FolderObjB.subFolders 'サブフォルダをコピー Fso.CopyFolder FolderB.Path, PathA Next 'お片付け Set FolderObj = Nothing Set Fso = Nothing End Sub
Excelでファイル操作というのは一般的なのでしょうか? FSO(FileSystemObject)を使えばできるということで書いてみましたが、あまりExcelに適した作業ではないように思えますが…? ただしもうちょっと肉付けして操作したファイル名をセルに入れていくなどすればExcelがログ代わりになりそうです。
こちらはFSOを使ったVBAなのでWindowsでのみ動作します。
Python
import os import shutil from tkinter import filedialog def get_file_path(msg): desktop_path = os.path.expanduser('~/Desktop') path = filedialog.askdirectory(initialdir=desktop_path, title=msg) return path path_a = get_file_path('移動元フォルダを選択') # 移動元フォルダ if not path_a: exit() path_b = get_file_path('コピー元フォルダを選択') # コピー元フォルダ if not path_b: exit() items_a = os.listdir(path_a) # 移動元フォルダの中身をリストアップ items_b = os.listdir(path_b) # コピー元フォルダの中身をリストアップ put_folder_name = "001" # フォルダ内に作成するフォルダの名前 if put_folder_name in items_a: # 「001」フォルダーがあったらリストから取り除く items_a.remove(put_folder_name) path_a_ = os.path.join(path_a, put_folder_name) # 「001」フォルダーのパス os.makedirs(path_a_, exist_ok=True) # 「001」フォルダーを作成(既存でもOK) for item_a in items_a: # ファイル・フォルダを移動 shutil.move(os.path.join(path_a, item_a), os.path.join(path_a_, item_a)) for item_b in items_b: # ファイル・フォルダをコピー item_b_ = os.path.join(path_b, item_b) if os.path.isfile(item_b_): # ファイルだった場合 shutil.copy2(item_b_, path_a) continue if os.path.isdir(item_b_): # フォルダだった場合 shutil.copytree(item_b_, os.path.join(path_a, item_b)) continue
Pythonはコードを書くのがいちばん楽でした。コピーするのにファイルかフォルダを判別してメソッドを使い分ける必要はありますが、それぐらいです。全体的に短いコードでサクッと書けるのがいいですね。
パス指定は tkinter というライブラリでフォルダ指定できますが、OSに合わせたパスをハードコーディングしてもいいと思います。
このコードはmacOSでもWindowsでも動作します。
実際の処理もWindows版のDirectCloudドライブで上記のコードを拡張したPythonプログラムで実行しました。
オンラインストレージのフォルダ構造構築、承ります。
クライアントとのファイルやり取りに便利なオンラインストレージですが、今回のように大量ファイルをディレクトリごとにアップロードするといった作業は手間がかかったりするものです。
タクトシステムではこういったオンラインストレージのフォルダ構造構築や自動アップロード作業の効率化など承っております。
また、オンラインストレージDirectCloudをお試しで使ってみたいというご要望あればお応えすることも可能ですのでこちらもよろしければお問い合わせください。