Moodle開発者フォーラム

Moodle2系統でMoodle1系統のように直接(実際には間接的に)巨大なファイルを転送する手段

 
1997年の頃の写真です
Moodle2系統でMoodle1系統のように直接(実際には間接的に)巨大なファイルを転送する手段
Translators

https://moodle.org/mod/forum/discuss.php?d=221652

上記のディスカッショントピックスにある問題はいずれ表面化すると多くの方が考えていたと思います.外部リポジトリを用いる方法もあるのでしょうが,もう少し何とかならないか.

すみません,いつもですと解決してから報告するのですが,今回はどなたか開発してくれないかな,という他力本願です.

  1. FTPやSambaなどを用いてサーバ上のmoodledata/bulkupload(←適当です)フォルダに直接,一つあるいは複数のファイルをアップロードする.なんでしたらコースIDでサブフォルダを作成した上でその中にアップロードする.
  2. Moodleの該当するコース上でボタンを一つクリックすると,moodledata/bulkupload/(コースID)フォルダ内の全ファイルをコースファイルの領域に取り込む

という機能です.いまでもアップロードするファイルなどはtempなフォルダに一度読み込まれた上でコースファイルの領域に書き込まれる(データベースにエントリされる)のだと思いますので,その仕組みをそのまま使うだけでしょう.

moodledata/bulkuploadフォルダの下にコースIDのフォルダを作成する機能も各コースの管理ブロックの中に「バルクアップロードフォルダの作成」(フォルダが存在しない場合のみenable)という項目を作成し,コースの管理者が作成する.そしてmoodledata/bulkupload/(コースID)フォルダ内にファイルやフォルダが一つ以上存在する場合は「バルクアップロードの取り込み」の項目がenableになる,という感じで.

どなたか春休みの宿題で取り組んでみませんか? ブロックで機能を実現するのも一つの手ですね.

#いやいや外部リポジトリを使った方が楽(←試していません)という反論も含めてご意見よろしくお願いします.

 
最大評点: -
1997年の頃の写真です
Re: Moodle2系統でMoodle1系統のように直接(実際には間接的に)巨大なファイルを転送する手段
Translators

以前に投稿した上記問題ですが,実際にMoodle2.4をインストールして使って見たところ,サイト管理内の”プラグイン”→”リポジトリ”→”ファイルシステム”で実現できていることに気付きました.インスタンスを追加することで,moodledata/repositoryの下に作成したフォルダ内のファイルをファイルピッカーでMoodle上にて参照可能でした.他にもWebDAVのリポジトリがありますので,これでも良いかも知れませんね(私の環境では,QNAPのNASとの間でWebDAVの認証が,まだ通っていませんが…).

 さて,これでMoodle2.4のサイトに対してApache/PHPの最大アップロードファイルサイズの制約を受けずにファイルのアップロードが可能であることは確認できました.

 ただ,ファイルピッカーという一方向(外部からMoodleサイトへのデータの送信のみ)のファイラーですと,今度は逆方向,Moodleサイトからクライアントの端末への巨大なファイルの転送ができません.ダウンロードの機能はあるのですが,たとえばコースバックアップファイルが11GBにもなってしまうと,うんともすんとも言いません(もっと小さなファイルならばダウンロードできることを確認しました).せめて何かしらエラーが出れば良いのですが.

 もしかしたら何か設定の見落としで,11GBのファイルであろうともダウンロードできるのかも知れませんが,MoodleのサーバとLANで直結できる環境にあったとしてもWebブラウザ経由でのダウンロードしかできないのは勿体ない.

 moodledata/filedirの下に格納されているファイルは,管理情報をDBに持っており,ファイル名(フルパス名込み)をハッシュ化されたファイルをサーバのmoodledata/filedirから探し出してきて手元にコピーするのも簡単ではありません.

 ちなみに,Moodle上の論理的なファイル格納領域内の特定のファイルのリアルなフルパス名を取得する関数は,moodle/lib/filestorage/stored_file.php内のclass stored_fileのprotected function get_content_file_location()です.そう,protectedであり,”NOTE: do not make this public, we must not modify or delete the pool files directly! ウインク"とのこと.DBの情報を削除しないでファイルだけ消すと不一致が生じるから,という心配はよく分かります.でも,ちょこっと見てみたいので,protectedをpublicに変えた,

    public function get_content_file_location2() {

         return $this->get_content_file_location();

     }

などという呼び出しを追加して実験をしてみました.その上で,moodle/lib/filelib.php内のfunction file_get_drafarea_files()に,

        foreach ($files as $file) {

              $item = new stdClass();

              $item->filename = $file->get_filename();

              $item->filepath = $file->get_filepath();

              $item->fullname = trim($item->filename, '/');

              $filesize = $file->get_filesize();

              $item->size = $filesize ? $filesize : null;

              $item->filesize = $filesize ? display_size($filesize) : '';

// (Shirai)

$item->contentpath = $file->get_content_file_location2();

// (Shirai)             $item->sortorder = $file->get_sortorder();

              $item->author = $file->get_author();

のように1行追加すれば,ファイルピッカーでサイト内のファイル(たとえば”リストア”でバックアップファイルエリアのファイルなども含めて)の情報(ファイル名その他)と一緒にリアルなフルパス名が得られるところまでは確認しました.ただ,ファイルピッカーのレンダラーがJavaScrioptなので私にはこれから先はお手上げでした….

 もちろん,冷静に考えれば私がしたいことといえば,ファイルピッカーでリストアップされるファイルを1個単位で構わないからmoodledata/tempフォルダあたりに書き出したいだけです.そうであれば,class stored_file内のpublic function copy_content_to()以下の関数を使えば良いだけなのですが.結局はファイルピッカーが”高級なプログラム”過ぎて手が出せずにいます.どなたか,ファイルピッカーの画面で,ダウンロードの代わりに(ユーザが管理者権限を持つ場合は)ファイルシステムにコピーする機能を組み込んで頂けないでしょうか.

あるいは逆方向(Moodleの論理ファイルシステムからリアルなOSのファイルシステムへ)にもファイルをコピー可能なブロックがあるよ,といった情報をお持ちの方,ご回答頂けますと助かります.

1997年の頃の写真です
Re: Moodle2系統でMoodle1系統のように直接(実際には間接的に)巨大なファイルを転送する手段
Translators
ふむ.ブラウザ(たとえばIE)のJavaScript(IEではアクティブスクリプトと表記)を無効にすると,ファイルピッカーで,moodle/repository/draftfiles_manager.phpが呼び出されますね.これならば手を加えることができるかも. (作業中) あ,できました.
1997年の頃の写真です
Re: Moodle2系統でMoodle1系統のように直接(実際には間接的に)巨大なファイルを転送する手段
Translators

具体的には,moodle/repository/draftfiles_manager.phpに,二か所の修正(追加)を施すだけです.

(1)135行目あたり

    break;
// (Shirai)
case 'copyToMoodledata':
    $destdir = $CFG->dataroot.'/temp/exportfile';
    if (!file_exists($destdir)) {
         mkdir($destdir);
    }
    if ($file = $fs->get_file($user_context->id, 'user', 'draft', $itemid, $draftpath, $filename)) {
        $file->copy_content_to($destdir.'/'.$filename);
    }
    $home_url->param('action', 'browse');
    $home_url->param('draftpath', $draftpath);
    redirect($home_url);
    break;
// (Shirai)

case 'downloaddir':

(2)340行目あたり

                $home_url->param('action', 'renameform');
                echo ' [<a href="'.$home_url->out().'" class="fm-operation">'.get_string('rename').'</a>]';
// (Shirai)
                $home_url->param('action', 'copyToMoodledata');
                echo ' [<a href="'.$home_url->out().'" class="fm-operation">'.get_string('copy').'</a>]';
// (Shirai)

                if (file_extension_in_typegroup($file->filename, 'archive', true)) {

 

以上の(Shirai)で挟まれたコードを追加した上で,ブラウザのJavaScriptを無効にします.そしてファイルピッカー(たとえば適当なコースに入り,コースの編集メニューの中からリストアを選択するなど)を呼び出します.すると地味な構成のファイルピッカーの画面に切り替わります.ここでファイルの一覧が…うちの環境だけなのかも知れませんが,表示されるまで分単位で待たされます….リスト表示される各ファイルの右に,「削除」「移動」「リネーム」,そのさらに右に「コピー」が追加されます.このコピーを押すと,そのファイルがmoodledata/temp/exportfileフォルダ(無ければ作成します)にコピーされます.ボタンを押しても何もアクションがありません.レスポンスもありません.続けざまに何度も押すとよろしくないと思います.おとなしく,サーバの端末を開いてexportfileフォルダの中にファイルがコピーされていくのを確認して下さい.

 現状,全くエラー処理がありませんし,管理者以外でも実行できてします.あくまでMoodleサイト上の巨大なファイルをサーバOSのファイルシステムにダイレクトにコピーしたい,という要望がある場合にのみ応えることができるパッチです.これを改良して使いやすくして頂けるのは大歓迎です.

 ただこのnon-JavaScriptのコードの完成度は高くないようで,「戻る」ボタンを押すとエラーが出たしります.あまりサポートするつもりは無いのではないでしょう.もしかしたらそのうちにファイルごと消されるかも?

1997年の頃の写真です
Re: Moodle2系統でMoodle1系統のように直接(実際には間接的に)巨大なファイルを転送する手段
Translators

しかし,11GB(これでも減らしたつもりだったのだが…)のコースをリストアしようと思ったのですが,ファイルピッカーのファイルシステムリポジトリでアップロードしたら無反応,と思ったら裏で地道にコピーを続けてくれていた.画面をF5で再描画したら現れましたのですかさず更新.これでユーザーバックアップファイルエリアにファイルをコピーできました.そしてそのファイル名の右の方にある「リストア」を押したのですが,Squidさんより「ノーレスポンスだ」というエラーが帰ってきてしまいました….何度もリストアのボタンを押したせいでしょうか,いま11GBのファイルが3つ,moodledata/temp/backupフォルダ内にコピーされました.しかし画面は一番最初に戻ってしまっているので,このコピーされたファイルを使って(セッションを保ったまま)リストアを続けることはできないでしょう.

どなたか巨大なバックアップファイルのリストアで同じ問題に遭遇して解決できた方はいませんか? apache/phpの設定を変更するしかないでしょうか.既にmax_execute_time(不正確)は1時間くらいに増やしてあるので,apacheの方の問題かも知れませんし,ゼロデータが返信されたというエラーはSquidが出しているので,そちらは私には手を出せませんのでお手上げでしょうか.何かダミーでも良いから返信すればごまかせる?

1997年の頃の写真です
Re: Moodle2系統でMoodle1系統のように直接(実際には間接的に)巨大なファイルを転送する手段
Translators

エラー自体は,「The requested URL could not be retrieved 

サイズがゼロの応答

このリクエストにに対してSquidは何もデータを受け取りませんでした。」です.

[リストア]ボタン(リンク)をクリックすると,

http://www.suzuka-ct.ac.jp/****/moodle2/backup/restorefile.php?action=choosebackupfile&filename=aimechroboken.mbz&filepath=%2F&component=backup&filearea=course&filecontextid=4109&contextid=4109&itemid=0

のようにバックアップファイルの情報を持ってrestorefile.phpを呼び出します.呼ばれた側のレスポンスが無いのがいけないのですよね.呼ばれた側のrestorefile.phpでは,パラメータの$action == 'choosebackupfile'ですので,以下のコードが実行されます.

// choose the backup file from backup files tree
if ($action == 'choosebackupfile') {
    if ($fileinfo = $browser->get_file_info($filecontext, $component, $filearea, $itemid, $filepath, $filename)) {
        $filename = restore_controller::get_tempdir_name($course->id, $USER->id);
        $pathname = $tmpdir . '/' . $filename;
        $fileinfo->copy_to_pathname($pathname); ← これがベラボウに時間が掛かる
        $restore_url = new moodle_url('/backup/restore.php', array('contextid'=>$contextid, 'filename'=>$filename));
        redirect($restore_url);
    } else {
        redirect($url, get_string('filenotfound', 'error'));
    }
    die;
}

バックアップファイルをmoodledata/temp/backupにテンポラリな名前でコピーし終えたら速やかにredirect()で次の画面に移れば(当面は)問題ないはず.ところが1バイトたりとも返答しないものだからプロキシサーバが”不到達”と判断する.

せめて,上記コードの後にある,

echo $OUTPUT->header();

くらいは出力しておいてくれれば状況が変わる?

ちょっと試してみましょうか.

 

1997年の頃の写真です
11.8GBのバックアップファイルがリストアできない
Translators

 タイムアウトが発生するのは,本校ではProxyを利用しており,これがタイムアウトしていました.そこで手動での作業中は$CFG->wwwrootを変更してProxyなしの直結でクライアントPCから作業を行っています.

 しかしいま別の問題に当たっています.11.8GBのコースバックファイルをリストアしようとしたのですが,全くピクリとも動きません.裏での動きを探るべく,詳細なログをファイルに記録するようにコードを追加して調べたところ,ZipArchiveのopen()で,「これはアーカイブファイルじゃない」というエラーを出しています.mbzという拡張子ではありますが,中身はZIPファイルです.ただ,ZIPファイルは標準ですと4GBが上限です.これを超えると内部のseekに使うインデックスがオーバーフローします.拡張されたZIP形式ではこの制限がもっと緩いはずですが,どうもZipArchiveライブラリでは圧縮はできても解凍はできない(と仮定).

 ためしに,CentOS6.3上のunzipで解凍しましたが,一部,エラーが出ます.ではp7zipならどうだ?と思ってインストールしたのですが,これでも同じくエラーが一部のファイルの解凍時に出ます.いま,JDEのjarコマンドで解凍しています.パッと見た感じではいまのところエラーが出ていません.

 いざとなったらリストアのコード中の解凍部分はjarをsystem()で呼び出すとして,どうなんでしょう,これは本当にPHP5.3.3のZipArchiveが4GB超えに対応できていないのが原因なのでしょうか.なお,filesize()関数では11.8GBのファイルであるにも関わらず,正しいファイルサイズを出力してくれており,これは新しいバージョン(あるいは64bitOSの?)の恩恵には浴しているようです,が.

 他にも4GBを超えるコースバックアップファイルのリストアが失敗する,あるいは成功した方がいらしたらご報告頂けないでしょうか.

1997年の頃の写真です
Re: 11.8GBのバックアップファイルがリストアできない
Translators

 解凍されたファイル数をp7zipのリスト表示で得られるファイル数と比較したところ数はあっている.したがって,ほぼ間違いなく全ファイルが解凍できたと思われる.ファイルサイズは厳密に比較していませんが.とりあえず,ZipArchiveではなくjarで解凍するようにコードを変更してみます.

1997年の頃の写真です
Re: 11.8GBのバックアップファイルがリストアできない
Translators

とりあえずコースのリストアは成功した模様.

仕事から戻ってきたらブラウザとWebサーバのコネクションが切れており,リストア成功の画面になっていなかった.詳細なログを記録しているのだが,それが途中で止まっているように見える.ただ,ログインし直して目的のコースをちょっと散歩した範囲では異常は見当たらない.コースファイルはきちんとレガシーな領域に展開され,フォルダリソースからのアクセスにも問題はなさそう.

 一応,環境の整理.

 サーバOS : CentOS6.3(final) / 64bit
 サーバ: ML115G5上のESXi4.1, メモリ2GB割り当て

この上にMoodle2.4をApacheとMySQL同居.PHP5.3.3.このZipArchiveライブラリによる11.8GBのmbz(Moodle2形式のコースバックアップファイル)の展開ができなかった.試しにコマンドラインでunzipを試すも一部のファイルが展開に失敗.p7zipでも同様(ただし展開に失敗するファイルの量は違う).ためしにJDE1.7.0をインストールし,jar を使って展開して見たところ展開に成功.そこでbackup/utill/ui/restore_ui_stage.class.phpのfunction extract_file_to_dir()内のreturn($fb->extract_to_pathname(..))をexec()でjarコマンドを呼ぶように変更したところ,ほぼリストアに成功.

 以後,きちんと最後の画面が出るように,原因追及を行う.

1997年の頃の写真です
Re: 11.8GBのバックアップファイルがリストアできない
Translators

一応,どういう改良を行って4GB超のmbzファイルを展開できるようになったのか,緊急で困っている人向けに残します.当然,jarコマンドがインストールされている前提です.エラーチェックはありません.

    protected function extract_file_to_dir() {
        global $CFG, $USER;

        $this->filepath = restore_controller::get_tempdir_name($this->contextid, $USER->id);

        $fb = get_file_packer();
// (Shirai) jar で解凍
//      return ($fb->extract_to_pathname("$CFG->tempdir/backup/".$this->filename, "$CFG->tempdir/backup/$this->filepath/"));
        $cwd = getcwd();
        $dest = "$CFG->tempdir/backup/$this->filepath";
        if (!file_exists($dest)) {
            mkdir($dest);
        }
        chdir($dest);
        $cmd = 'jar xf '."$CFG->tempdir/backup/".$this->filename;
        exec($cmd, $output, $ret);
        chdir($cwd);
        return true;
// (Shirai)
    }

赤い部分が追加した部分です. 

画像 Toshihiro KITA
Re: Moodle2系統でMoodle1系統のように直接(実際には間接的に)巨大なファイルを転送する手段
Particularly helpful MoodlersTranslators
色々ご苦労様です。笑顔

pdf2submssion の pdfscan.php の submit_file() では、
create_file_from_pathname() を使って、ファイルを Moodle の中に送り込むことが出来ているので、
その逆をすればいいはずですね。
http://docs.moodle.org/dev/File_API
の Read file あたりを参考にして。

具体的には、例えば、バックアップファイル .mbz を、取り出したいということでしょうか?
# ちょっと今すぐに作ってみますよ、とは気軽には言えないのですが...

1997年の頃の写真です
Re: Moodle2系統でMoodle1系統のように直接(実際には間接的に)巨大なファイルを転送する手段
Translators

一応,4/11の「具体的には,moodle/repository/draftfiles_manager.phpに,二か所の修正(追加)を施すだけです.」の方法でファイルの抜出には成功しました.

あと,バックアップファイルに限定ですが,完成したmbzをMoodleのファイルストアに書き戻す間に,こっそりとNAS側でコピー(moodledataをNASに置いています.そしてNAS上でコピーが可能で,こちらの方が速度が圧倒的に速い)する方法,あとはunlink()させないで残しておく方法など,セコイ手はいくつかあります^^; でもやはりどなたか作ってくれるとうれしいですね!

YSOUWA60年
Re: Moodle2系統でMoodle1系統のように直接(実際には間接的に)巨大なファイルを転送する手段
 

看護大学の橋本です.

詳細な説明ありがとうございました.100本以上ある3GB以上の動画ファイルをどうしようか困っておりました.

 

感謝です.