読者です 読者をやめる 読者になる 読者になる

単純な画像掲示板

MENTAの練習で、単純な画像掲示板を作成しました。

MENTAの解凍とリネーム


下記の手順で解凍して、ディレクトリ名を変更します。

$ tar xvzf MENTA-0.15.tar.gz
$ mv MENTA-0.15 simple_image_bbs
$ cd simple_image_bbs

プラグインの作成


各コントローラで使用する共通の関数を定義します。
ここでは、プラグインの形として作成しました。
「plugin/」ディレクトリに、「image.pl」というファイルを作成しました。
ファイルの中身は下記のようなものです。

package MENTA::Plugin::Image;
use MENTA::Plugin;
use Cwd;


my $IMAGE_BASE = 'image';
my $IMAGE_DIR = Cwd::cwd() . '/app' . MENTA::static_file_path() . $IMAGE_BASE;


sub image_base { return $IMAGE_BASE; }
sub image_dir { return $IMAGE_DIR; }


画像の保存ディレクトリを返す関数を定義しています。
プラグインを作成するルールとして、下記のようなものがあるようです。

  • package名は「MENTA::Plugin::{名前}」という形にする
  • MENTA::Pluginをuseする
  • 関数名は、プレフィックスにファイル名を付ける(image.plだったらimage_)

コントローラの作成


まず、一覧ページのプログラムとHTMLです。

use MENTA::Controller;

use File::Basename 'basename';

sub run {
    my $image_dir = image_dir();
    my @images = map { basename($_) } glob("$image_dir/*");
    @images = sort {$b cmp $a} @images;
    render_and_print('index.mt', \@images, $image_dir);
}


index.plで、先ほど作成したプラグインの関数「image_dir()」を呼び出していますね。

? my ($images, $image_base) = @_;
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
  <title>単純な画像掲示板</title>
 </head>
 <body>
  <h1>単純な画像掲示板</h1>
  <form method="POST" action="<?= uri_for('upload') ?>" enctype="multipart/form-data">
   <div>
    File name:
    <input type="file" name="image">
    <input type="submit" value="Upload">
   </div>
  </form>
 <div>
? foreach my $image (@$images) {
  <div>
   <hr>
   <div>画像名: <?= $image ?></div>
   <div>
    <img src="<?= static_file_path("image/$image") ?>">
   </div>
  </div>
? }
  </div>
 </body>
</html>


次に画像アップロード機能です。「upload.pl」という名前で作成しました。

use MENTA::Controller;
use DateTime;
use CGI;


sub run {
    my $cgi = CGI->new;
    my $image_handle = $cgi->upload('image');


    my $image = '';
    my $buffer = '';
    while ( read( $image_handle, $buffer, 1024) ) {
        $image .= $buffer;
    }

    unless ($image) {
        return render_and_print('error.mt', 'Upload fail. File is not specified.');
    }
   
    my $upload_max_size = 3 * 1024 * 1024;
   
    if (length $image > $upload_max_size) {
        return render_and_print('error.mt', 'Upload fail. Image size is too large.');
    }

    my $image_name = $cgi->param('image');
    $image_name =~ /\.(\w+)$/;
    my $image_type = $1;

    my %valid_types = map {$_ => 1} qw(gif jpeg png jpg);

    unless ($valid_types{$image_type}) {
        return render_and_print('error.mt', 'Upload fail. Content type is wrong.');
    }

    my $exts = {'gif' => 'gif',
                'jpeg' => 'jpg',
                'png' => 'png',
                'jpg' => 'jpg'};
    my $ext = $exts->{$image_type};

    my $image_dir = image_dir();
    my $image_file = "$image_dir/" . create_filename(). ".$ext";

    while (-f $image_file) {
        $image_file = "$image_dir/" . create_filename() . ".$ext";
    }


    open(FILE,'>',$image_file);
    print FILE $image;
    close(FILE);

    redirect('/');
}


sub create_filename {
    my $dt = DateTime->now( time_zone => 'Asia/Tokyo' );

    my $rand_num = int(rand 100000);

    my $name =
        $dt->strftime("image-%Y%m%d%H%M%S-").
        sprintf("%05s", $rand_num);

    return $name;
}


「app/static/」ディレクトリの「image/」ディレクトリの中に画像を保存します。


実行


「./bin/cgi-server.pl」を起動して「http://localhost:5555/」にアクセスするとページが表示されます。
f:id:memememomo:20100402164234p:image

うまく行かなかった部分


アップロード処理の部分で、どうもうまく行かなかった部分がありました。
上記のupload.plでは、ファイルハンドルを受け取る処理に下記のような感じで行なっています。

    my $cgi = CGI->new;
    my $image_handle = $cgi->upload('image');


CGI.pmを用いてやっています。こちらはうまく行っています。
ただMENTAにはupload()関数があり、この関数でファイルハンドルが取ってこられるらしいのです。しかし、返ってくる値はnullだけでした。

   # こうできるはず?
   my $image_handle = upload('image');


upload関数の中身ではHTTP::Engine::Requestのuploadメソッドに委譲する形になっています。
HTTP::Engine::Request::uploadのソースを覗いてみますと、ぱっと見、ファイルハンドルを返すような記述がありませんでした。もしかして、何か用途を間違っているのでしょうか・・・。


参考文献


かんたんプログラミング CGI/Perl

かんたんプログラミング CGI/Perl


前回の簡単な掲示板に引き続き、本書を参考にさせていただきました。
Mojolicious::Liteでの実装はこちら