Windowsサーバ上でファイル圧縮にzipを使う

Windowsサーバ上でファイル圧縮にzipを使う

- Tatsuya Shirai の投稿
返信数: 19

 ファイルの総量が多いコースのバックアップがタイムアウトする場合に関する話題から派生しました.
http://moodle.org/mod/forum/discuss.php?d=114838#p505940

 Windowsをサーバとして用いている場合に限定した話題ですが,Linuxをお使いの方からはアドバイスを頂けると助かります.私は主にWindowsを利用していますので,間違ったことを書く可能性があります.


 Moodleは標準ではPHP言語で記述されたZIP書庫作成用のライブラリ(pclzip)を用いて複数のファイル(フォルダを含む)を一つのZIP書庫に格納および圧縮します.もしコースのバックアップ(ファイルの圧縮)に時間が掛かり過ぎるとPHPのスクリプトに対するタイムアウトが発生して処理を中断し,そのコース定期バックアップが”未完”となります.

 この問題を解決するには,

  • コースファイルの数と量を減らす
  • タイムアウトの制限値(max_execution_time)を延ばす
  • サーバの性能を上げる

に加え,pclzipライブラリではなく外部ZIPコマンドを利用するという選択肢があります.PHPのタイムアウトはスクリプトのバグ(無限ループなど)に対する防衛措置であるため,外部コマンドの実行に要する時間計測は行なわないため,外部コマンドで圧縮を行なう処理に時間が掛かってもエラーにならないためです.

 外部zip(unzip)コマンドをMoodle上で使用するには,[サイト管理]-[サーバ]-[システムパス]の”zipのパス”(と必要ならば”unzipのパス”)を入力します.この設定が行われている場合,lib/moodlelib.phpのfunction zip_files()関数はpclzipライブラリではなく外部コマンドを利用します.なお,[システムパス]のページでは,”(Unix/Linuxのみ,任意)”とコメントがありますが,Windowsでも設定は可能ですし,そのためのコードもfunction zip_files()には記述されています.これはLinuxの場合は標準でZIPl書庫を作成するコマンドラインのコマンドがインストールされている場合が多いのに対して,Windowsは標準では搭載されていないためでしょう.


では,WindowsでコマンドラインによりZIP書庫を作成するには?

 多くのWindowsユーザはアーカイバと呼ばれるプログラム等を用いてグラフィカルなインタフェース上で複数のファイルやフォルダを一つのZIP書庫に変換,あるいは展開していると思います.私もそうです(ExpLzhを使用).MS-DOSを使ったことのある方ならば,LHAやPKARCなどの名前を憶えていると思います.LHA.EXEはいまでも使うのですが,PKWare関係はほとんど使わなかったので記憶が曖昧です.

 ともかくWindows上で外部の圧縮/展開プログラムを利用するには何かをインストールする必要があります.gzipというものもWindows版がありますが,これは単一のファイルを圧縮するものですので,複数のファイルを束ねて圧縮するにはTARというプログラムと組み合わせる必要(tar.gz)があります.これではありません.zip(Win32版)を用います.Moodleの管理者用ドキュメントを参照したところ,どうやら以下のものを用いると良いようです.
ZIP for Windows : http://gnuwin32.sourceforge.net/packages/zip.htm


以下,実際に作業(試行錯誤)を行ないながら途中経過を記録していきます.間違ったことを記述している可能性もありますので,「成功した」と私が書くまではお手元で試すのは自己責任でお願いします.

  • オプションはLinux版と同じか?
  • 日本語ファイル名やフォルダ名を圧縮対象のファイル名として利用できるか?
  • 日本語ファイル名をZIP書庫名として用いることができるか?
  • 大きなZIP書庫を作成できるか?(2GBを越える)

この辺りが調査対象です.申し訳ありませんが,標準のMoodleパッケージではなく,fs_moodle(日本語Windowsにも対応した白井オリジナルのパッケージ)での実験結果のみです.また,Moodleのバージョンは1.9.3+です.

Tatsuya Shirai への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Tatsuya Shirai の投稿

 まず,インストーラ版のzipパッケージをダウンロードします.

 http://gnuwin32.sourceforge.net/packages/zip.htm の DownloadにあるComplete package, except sourcesの[Setup]です.実行にはDLLを必要とするようですので,Binariesでは自分で必要なDLLをインストールする必要があるためです.

 2009/02/02現在ではzip-3.0-setup.exeというファイル名です.

 ダウンロードしたzip-3.0-setup.exeをダブルクリックしてインストールします.標準では”C:\Program Files\GnuWin32”というフォルダにインストールされます.パス名に半角空白文字を含むのはイヤだという場合はインストール時に別のフォルダ名(例えばC:\GnuWin32)に変更しても良いでしょう.ここでは標準のフォルダにインストールしたとして話を進めます.

 C:\Program Files\GnuWin32\binには,bzip2.dll,zip32z64.dll, zip.exe, zipcloak.exe, zipnote.exe, zipsplit.exeが存在します.実際に利用するのはzip.exeだけでしょう.しかしインストールしただけではこのコマンドにはパスが届いていませんのでWindowsのコマンドプロンプトから「zip」とキー入力してコマンドを呼び出すことはできません.もしコマンドラインからzip.exeを使用したいのであればパスを通す必要があります.今回はMoodleから使用することのみを考えていますので,パスの指定(追加)については省略します.


(実験)コマンドラインでzip.exeを使って日本語ファイル名/フォルダ名が使えるのか?

 C:\Program Files\GnuWin32\bin

に日本語を含むフォルダを作成し,その中に日本語をファイル名に含む複数のファイルをコピー.そのフォルダを日本語を含むファイル名のZIP書庫に圧縮してみました.一応,正しく日本語のファイル名を扱えるようです.

> zip -r ZIP書庫名.zip フォルダ名

Tatsuya Shirai への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Tatsuya Shirai の投稿

 次はMoodleからインストールしたGnuWin32版のzipコマンドを使うように設定を変更します.

 Moodleに管理者でログインし,[サイト管理]-[サーバ]-[システムパス]を開きます.

 zipのパスに,”C:\Program Files\GnuWin32\bin\zip.exe”(ダブルクオーテーションは入力不要です)と入力します.そして[変更を保存する]をクリックして保存します.さぁ,これで使えるはずです.

 どこか適当なコースに移動し,[管理]-[ファイル]でそのコースのコースファイルのフォルダを開きます.backupとmoddataのフォルダはシステムで使用するフォルダですので実験には使わないように気を付けて下さい.特にmoddataのフォルダを誤って消してしまうと大変なことになります.ファイルを圧縮する際に,オリジナルのファイルやフォルダは削除されないハズですが,万が一のトラブルに備えて,実験用のフォルダとファイルを新規作成して試すことを勧めます.

 いくつかフォルダやファイルを選択(ファイル名左のチェックボックスをチェックする)して下さい.次に”選択したものを...”のプルダウンメニューを引き下ろして,”zip書庫を作成する”を選んで下さい.対象となる全てのファイル名がリスト表示されます.”Zipファイルの名称は?”の欄にファイル名(例えば日本語を含むファイル名)を入力して下さい.拡張子を省略した場合は自動的に.zipが付加されます.

 そして”zip書庫を作成する”のボタンを押すと圧縮開始です.

 WindowsXP SP3 (32bit)とfs_moodle版のMoodle1.9.3+では正常にzip書庫が作成されました.試しにリストや展開を試して見て下さい.なお,展開する際には新たに新規作成したフォルダにzip書庫をコピーして,それを展開して下さい.ZIP書庫の展開時に圧縮前のファイルを上書きしてしまうからです.オリジナルのファイルと同一のファイルが展開されたのであれば成功です.

Tatsuya Shirai への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Tatsuya Shirai の投稿

(オプション指定について)

 lib/moodlelib.phpのfunction zip_files()では,サーバOSがWindowsの場合は,

 cd (zip書庫を作成するフォルダ) & (zip.exeのフルパス名) -r (作成するzip書庫名) (圧縮するファイル/フォルダのリスト)

をExec()で呼び出しています.(Windows以外の場合はマルチステートメントの句切り子は&ではなく;です)

 この呼び出しを行なう直前で,パス区切り / を \ に置き換えています.もしかしたらこれが悪さをする可能性があります.fs_moodle3.10.00でもこの問題は特に対策を行なっていませんので,調査の上,修正が必要であれば次のバージョンで対策を施します.

Tatsuya Shirai への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Tatsuya Shirai の投稿

問題であった総量が多いコースファイルを含むコースのバックアップが可能かどうか,は,いま実験しています.外部コマンドでも処理に随分と時間が掛かるようです.

 まず圧縮対象のファイルをテンポラリフォルダ(moodledata/temp/backup)にコピーし,それを圧縮.圧縮が終わったらコースにコピーして,成功したらテンポラリフォルダを削除する.つまり20GBのデータを含むコースをバックアップするには,オリジナルの20GBに加えて,一時ファイル20GB,完成した書庫ファイル(<20GB),コースにコピー(<20GB)が瞬間的にHDD上に存在することになります.HDDに十分な空き領域(この例ですと60GB弱)が必要です.

 いま,まさに20GBのコースファイルを持つコースのバックアップを手動で行ないましたが,5分経っても一時ファイルのコピー中です...

 一時間以上掛かって,19GBのバックアップファイルが完成しました.多分,これからコースにコピーをするのでしょう.

Tatsuya Shirai への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Tatsuya Shirai の投稿

 moodledata/temp/backupの下に作成された一時フォルダ内にbackup-e-design-20090202-1249.zipのようにバックアップは作成されましたが,それが元のコースにコピーされませんねぇ.ブラウザの画面は「この画面を表示できません」に変わっています.もしかしたらセッションが途切れたのか,その他の要因(Squid絡み)かも知れません.PHPから呼び出されたとcmd.exe(コマンドプロンプト)がタスクマネージャのリストに残っていますので,もしかしたらzip.exeがエラーを出していたのかも知れません.

 コマンドプロンプトを起動し,手作業でzip.exeを動かしてみました.標準出力に色々と出力されます.もしかしたらこれをサイレントにした方がいい?

Tatsuya Shirai への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Tatsuya Shirai の投稿

 試しにcronによる定期バックアップを外部zipプログラムに任せてみました.

 やはり20GBのコースファイルを持つコースは失敗しますね.ただ,ログでは以前は”未完”であったのが”エラー”に状態が変わりました.コースのbackupフォルダには(手動バックアップではコピーされなかった)backup-****.zipファイルがコピーされています.ただし,手動で作成したときのzipファイルのサイズが19GBであったのに対して,コピーされているファイルのサイズは思わせぶりな4GBです.できあがったzipファイルの中身のチェック(リストと正当性のチェック,および展開)をこれから行ないます(ファイルサイズが大きいので時間が掛かります).

 手動でのバックアップ(コースの管理メニュー経由のバックアップ)が完了しない理由は,もしかしたらSquidを通してアクセスしているせいかも知れません.確かタイムアウトが設定されていて制限時間を越えて何も送られてこないとコネクションを切断されたような記憶があります.いま並行してSquidを通さない経路で手動バックアップを試しています.

Tatsuya Shirai への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Tatsuya Shirai の投稿

 4GBのサイズしかないzip書庫(本来は20GBのコースファイルがある)を調べてみました.コメントがあると表示されたのですが,文字化けしていて一単語も読めませんでした(ANKですらない?).一応,リストの表示はされましたので,データ構造が完璧に壊れているわけでは無さそうです.いま,そのZIP書庫を今度は展開してみようとしたところ,次のボリューム(拡張子がz01)を指定して下さいと言われました.どうやらマルチパートのZIP書庫になっているようです.やはり途中でぶったぎられているのでしょうか.だとすると,誰が4GBまでコピーしたところで諦めたのか.

 手動でのバックアップをSquidを通さずに行なった実験も失敗です.コースファイル等を含んだ20GBのZIP書庫がmoodledata/temp/backupフォルダに放置されたまま,Apacheが異常終了し,ブラウザも「Internet Explorer ではこのページは表示できません」と表示した状態で止まっていました.

 ある程度のサイズのコースであれば外部ZIPプログラム(zip.exe)でバックアップできそうですが,それを超えるとApacheまで巻き込んで異常終了してしまうのでは困りましたね.Windowsの限界でしょうか.


白旗です.

どなたか御意見・情報提供よろしくお願いします.

Tatsuya Shirai への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Umeda Junichi の投稿

白井様

こちらのサイトでも実施して見ました。
コースファイルにDVDイメージを2個ほど置き、コースのバックアップ(トータル5GB程度)

タスクマネージャでzip.exeのCPU使用率を確認しつつzipファイルの作成を確認
4.8GBほどのZIPファイルが出来た時点で、zip.exeがタスクから消滅
apache.exeの使用率が増加し、一応処理が正常完了・・・

・・・したかに思えましたが、バックアップフォルダに保存されたファイルを見ると680MB程度しかなく
tempにあったzipファイルも消滅していました。

いろいろ見ていたらこのような情報がありました。(4GBを超えるzipは作れない模様・・・)
http://www.info-zip.org/FAQ.html

4GBを超える事ができて、コマンド可能な圧縮ソフトはこれが有名の様です。
http://sevenzip.sourceforge.jp/

オプションが若干違うようなのでいま実装してみております。

Umeda Junichi への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Tatsuya Shirai の投稿

 Umedaさん,ご協力ありがとうございます.なんとか元気が出てきました.

 当方では4GBを越えるzipファイルは作成できています.

 もう一度,定期バックアップを試してみましたが,やはり19.270.629KBのZIP書庫は完成するものの,それをコースのbackupフォルダに4,194,304KBしかコピーしてくれません.

 やはりこれはコピーに問題があるのではないのか.

 手動はともかくcronでのバックアップの実体はbackup/backuplib.phpのfunction backup_execute()です.この中から,moodledata/temp/backupの下に作られたユニークIDの名前のフォルダ内の全データをzip書庫に圧縮するfunction backup_zip()関数を呼び,それが完了したらfunction copy_zip_to_course_dir()でコースにコピーします.

 function copy_zip_to_course_dir()は同じくbackup/backuplib.php中にあり,コピー先のフォルダの準備を終えたら孫受けのfunction backup_copy_file()を呼びます.function backup_copy_file()は比較的汎用な関数のようです.もう単純にcopy()関数を呼んでいるだけ.


 実験してみました.19.270.629KBのzipファイルを単にcopy()関数でコピーするだけのPHPスクリプトを書いて.そうしたらやはり4GBまでコピーしたところで終了です.もしかして,これはPHP(Win32版)の制限なのでしょうか.心配になって検索してみたのですが,特にそのような記述が見当たらない(情報が多過ぎてうまく絞り込めない)ので自信が無かったのですが,実験をしてみてほぼ確信しました.

Tatsuya Shirai への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Tatsuya Shirai の投稿

 そういえばPHP上で巨大なファイルを取り扱う上での問題点は自分で以前に書いていましたね.当初はファイルサイズの表記だけの問題かと思っていたら2GBあたりからZIP書庫もおかしくなる,と(pclzipの場合).
http://moodle.org/mod/forum/discuss.php?d=102895

 てっきり,copy()関数はOSに処理を全て下請けしていると確信していたのですが,コピー元やコピー先にURIが指定できるなど,高機能な関数ですので自分でファイルポインタを管理してチキチキとコピーしているのかも知れません.その結果,2GBあたりから挙動が怪しくなり,4GBが限界になっているのかも知れません(当方,PHP5.2.0).

 zip書庫化は外部のzip.exeに任せれば問題無いのですから,OSが管理可能な領域へのファイルのコピーである今回の件も,Execute()でOSのcopyコマンドにファイルの複写を任せれば,もしかしたらうまく行くのかも知れません.よし,後日試してみます.


 Linux版のPHPではどうですか? 4GBを越えるファイルのコピーをPHPのcopy()関数で行なえます? > Linuxの方々.

Tatsuya Shirai への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Umeda Junichi の投稿

白井様

お世話になっております。

少々強引ではありますが、ひょっとしたら解決できそうな方法がありました。

zipファイル作成後、copyでファイルを移動する代わりに、renameにてファイルを移動(見かけ上)
させては如何でしょうか?(どちらにしろ元ファイルは消してしまうのですし)

単純に、rename()関数を使って4GB以上のファイルを別フォルダに移動してみたところ正常に動作する事が確認とれました。

Umeda Junichi への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Tatsuya Shirai の投稿

 なーるほど!

 それならばOSごとにコピーするコマンドの違い(まぁ,Windowsだけcopyなのですが...)を意識しないで済みそうですね! 助かります.

Tatsuya Shirai への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Umeda Junichi の投稿

すいません、状況を勘違いしていましたね(--;

仕様上の制限とすると、4GBを超えると分割するようにしないといけなさそうですかね。
(現実的なファイルの扱い的にもその方が良さそうではありますが・・・)

Umeda Junichi への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Tatsuya Shirai の投稿
 pclzipライブラリを使う場合は(分割が可能ならば)分割しないといけないでしょうが,zip.exeは私の環境では4GBを越えるZIP書庫も作成できました.OSはNTFSならば4GB(2GB?)の制限がありませんからね.
Tatsuya Shirai への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Umeda Junichi の投稿

白井様

4GBのファイルが出来ていなかったというのは恐らく私の思い込みのようです。

一瞬、5GB近くのファイルが見えた後、ファイルが消えたので多分作成まではOKだったのかと思われます。(ファイルが破損しているからNGなんだとかってに思い込んでしまいました)

ココからは想像になってしまいますが、copy()関数を使った時に転送されるファイル容量は、4GB以上のファイルをPHPが認識できたファイルサイズ分だけ転送されているのではないかと思われます。

例えば、私が試したファイルでは下記の様な現象になりました。
    元ファイルサイズ 4.57GB (4,910,464,650B)
    元ファイルのMoodle上での表示 587MB
    copy関数でコピーされたファイルサイズ 586MB(615,497,354B)

テストプログラムではこの状態で"コピーが成功した"(copy()=TRUE)と返されました・・・





Umeda Junichi への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Tatsuya Shirai の投稿

> 例えば、私が試したファイルでは下記の様な現象になりました。
>    元ファイルサイズ 4.57GB (4,910,464,650B)
>    元ファイルのMoodle上での表示 587MB
>    copy関数でコピーされたファイルサイズ 586MB(615,497,354B)

これでドンピシャリでしょうね! 一番最初にPHPで大きなファイルを扱うのに問題がありそうだと感じたのはfilesize()関数でした.OSから受け取ったファイルサイズの情報をint(32bit?)に格納するところでオーバーフローかなにかを起こしてしまい正しいファイルサイズにならない.ファイルを取り扱う場合にはその情報に基づいてファイルポインタを動かすので,取得したつもりのファイルサイズよりも先にはポインタが進まない.したがって,ランダムアクセスだけでなく,単なるコピーすら正確なサイズまで転送されないということでしょうね.これはLinuxでも特別なオプションを付けてコンパイルしていない限りは発生する問題だと思われます.

 私の場合は19GBのファイルサイズが,偶然,4GBまでファイルサイズを扱えるようにオーバーフローしていたのですね.Moodleはzipファイルをコピーした後にフォルダごと削除してしまうので,うまい具合に異常終了でもしてくれない限りはオリジナルの5GBのzip書庫の存在は分からないでしょうね.

 とても納得できました.ありがとうございます.

Tatsuya Shirai への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Umeda Junichi の投稿
stream_copy_to_streamを使って転送バイトを強制してみましたが、結果的には指定した数値もINT型で扱われている為、指定した値がオーバーフローした値までしか転送されませんでした。

再コンパイルなしではかなり厳しい状況のようですね・・・
Tatsuya Shirai への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Tatsuya Shirai の投稿
 cronによる定期バックアップを再び行いました。zip書庫の作成には外部のzip.exeを用い、完成したzip書庫をコースにコピーを行う処理はPHPのcopy()関数ではなくrename()関数を使うように改造してあります。コースファイルは約20GB、これを圧縮すると19GBのzip書庫になります。
 結論として成功しました。バックアップするコースに19GBのzipファイルがきちんとコピーされ、バックアップのログでも正常に一連の処理が行われたと記録されていました。
 ただし、PHPは2GBを越えるファイルサイズを正常に表示できませんので、Moodleのファイルエディタでファイルのリストを表示すると2,4GBと表示されます(当方ではオリジナルに改良を加えていますので4GBまでは正常に表示可能です)。サーバ上のエクスプローラで19GBのサイズであることを確認しました。

まとめると、
 PHPは2GBを越えるファイル操作が苦手である(rename()を除く)。
 pclzipでは大きなzip書庫を作成できないが外部プログラムを用いれば20GBのzip書庫でも作成可能である。
Tatsuya Shirai への返信

Re: Windowsサーバ上でファイル圧縮にzipを使う

- Tatsuya Shirai の投稿
外部zipコマンドを用い,copy()関数の代わりにrename()を使うことでコースファイルを含めて2GBを越えるコースのバックアップも不可能では無いと考えていますが,どうやらリストアで失敗するようです.
http://moodle.org/mod/forum/discuss.php?d=61847#p508116

fs_moodleでは指定したタイプのファイル(サイズ,ファイル名:正規表現OK,拡張子)をzip書庫化しない機能を搭載することで定時バックアップを失敗させない仕組みを実装しましたが...

新しいコースに既存のコースを複製する際には,バックアップしてリストアする時はコースファイルを含めずに手動でバックアップを取り,リストア後にコースファイルをコピーするといったテクニックが必要です.でも,この手はMoodle2.0では使えないですね(独自のファイルシステムを用いるため).

Moodle2.0で,2GBを超えるファイルを取り扱えるように何らかの工夫が施されれば良いのですが.たとえばcopy(), filesize()は用いず,独自のコマンド(OS標準のコマンドをsystem()で呼び出すなど)を用意してくれるなど.

#PHPに「このファイルは2GBを超えているから正常にコピーができない,ファイルサイズを取得できない,fread()なども無理」ということを教えてくれる関数を設けて貰うか,intの束縛から解放される拡張が行われないと,もう無理です.