• このエントリーをはてなブックマークに追加


はじめに

お仕事しているグループ内での情報の共有・伝達の一手段として,積極的にスクリーンショトを使ってもらいたい(画像添付すれば,何十行も文章書かなくても一発だろうが!),と思い始め,「撮影 → アップロード → URL取得」の手段が素早い Gyazo を普及できないかどうか考えてます.

ただ,いくら URL が複雑だからとはいえ,public な場所に置くのはアレなので,グループ内でのみ参照できるような場所に同じようなアプリケーションを置きたいところ.

そんな感じで以下,ごく簡単な Gyazo サーバ的アプリケーションを書いた際のメモです.

目次

目標とした環境,Gyazo クライアントの設定変更方法,そして,簡単 Gyazo サーバアプリケーションの実装,の順に触れていきます:

  • 目標とする環境
  • Gyazo クライアント for Mac
  • Gyazo クライアント for Windows
  • サーバアプリケーションのコード
  • サーバアプリケーションの準備: app.psgi
  • サーバアプリケーションの準備: /upload
  • サーバアプリケーションの準備: /img
  • サーバアプリケーションの起動
  • おためし
  • わざわざリバースプロキシさせた背景

目標とする環境

  • アプリケーションを plackup --port=5999 で立ち上げる
  • ベース URL: http://gyazo.q-chan.local/ (先のアプリケーションへリバースプロキシする.こうした背景は後述)
  • アップロード先: http://gyazo.q-chan.local/upload
  • 画像閲覧: http://gyazo.q-chan.local/img/{sha1}

Gyazo クライアント for Mac

本家のものを使います.

設定変更

/Applications/Gyazo.app~/Desktop/mygyazo.app としてコピーした後,mygyazo.app 内のファイル Contents/Resources/script の内容を次のように変更しました.

--- /Applications/Gyazo.app/Contents/Resources/script	2013-06-04 11:00:51.000000000 +0900
+++ /Users/issm/Desktop/mygyazo.app/Contents/Resources/script	2013-08-02 08:39:51.000000000 +0900
@@ -47,8 +47,9 @@
 # upload
 boundary = '----BOUNDARYBOUNDARY----'
 
-HOST = 'gyazo.com'
-CGI = '/upload.cgi'
+HOST = 'gyazo.q-chan.local'
+PORT = 80
+CGI = '/upload'
 UA   = 'Gyazo/1.0.2'
 
 data = <<EOF
@@ -69,7 +70,7 @@
   'User-Agent' => UA
 }
 
-Net::HTTP.start(HOST,80){|http|
+Net::HTTP.start(HOST,PORT){|http|
   res = http.post(CGI,data,header)
   url = res.response.body
   IO.popen("pbcopy","r+"){|io|

これで,~/Desktop/mygyazo.app を起動すれば,変更した設定に基づいた挙動になってくれます.

Gyazo クライアント for Windows

※ Windows 7 でしか確認していません.

Gyazo の公式クライアント (以前は Gyazowin)として)を入れてみましたが,Mac 版のような設定変更はどこで行うのだろ?ということになって,ググってみたところ,設定ファイルを ini ファイルに持たせるよう改造した gyazowin+ なるものがあることを確認:

さらに,リリース以降更新されていない gyazowin+ へ,gyazowin の最新状態をマージしたものまでありました:

ので,これを使うことにします.

設定変更

実行ファイル( gyazowin.exe )と同列にあるファイル gyazowin+.ini の内容を変更します.

--- gyazowin+.ini.default	2013-08-02 00:57:05.000000000 +0900
+++ gyazowin+.ini	2013-08-02 01:55:47.000000000 +0900
@@ -1,6 +1,6 @@
 [gyazowin+]
-upload_server=gyazo.com
-upload_path=/upload.cgi
+upload_server=gyazo.q-chan.local
+upload_path=/upload
 
 use_ssl=no
 ssl_check_cert=yes
@@ -9,9 +9,9 @@
 auth_id=
 auth_pw=
 
-up_dialog=yes
+up_dialog=no
 copy_url=yes
-copy_dialog=yes
-open_browser=no
+copy_dialog=no
+open_browser=yes
 
 use_clipboard_base=no

サーバアプリケーションのコード

GitHub に上げていますのでご参考程度にどうぞ:

サーバアプリケーションの準備: app.psgi

ディスパッチ処理がメンドイので,パスでアプリケーションを分割する形にしました.

use 5.16.0;
use warnings;
use Plack::Builder;
use File::Basename;
use File::Spec;
use lib File::Spec->catdir(dirname(__FILE__), 'lib');
use MyGyazo::Web::Upload;
use MyGyazo::Web::Image;
 
builder {
    enable 'ReverseProxy';
    mount '/upload/' => MyGyazo::Web::Upload->to_app();
    mount '/img/' => MyGyazo::Web::Image->to_app();
    mount '/' => sub {
        return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'hello' ] ];
    };
};

サーバアプリケーションの準備: /upload

アップロードされた画像を処理する部分です.

  • POST メソッド以外は弾く
  • ユーザ ID とその時点での時間を基に,画像 ID を生成する
  • 画像 ID を名前に,アップロードされた画像を保存する
  • 画像表示用の URL をレスポンスに含める

やっていることはこれだけです.

package MyGyazo::Web::Upload;
use 5.16.0;
use warnings;
use utf8;
use MyGyazo::Util;
use parent 'Plack::Component';
use Plack::Request;
use File::Basename;
use File::Path qw/make_path/;
use Time::HiRes ();
use Digest::SHA1 qw/sha1_hex/;
use File::Copy;
 
state $config = config();
 
sub call {
    my ($self, $env) = @_;
    my $req = Plack::Request->new( $env );
    my $up = $req->uploads->get( 'imagedata' );
 
    if ( $req->method ne 'POST' ) {
        return [
            405,
            [ 'Content-Type' => 'text/plain' ],
            [ 'method not allowed' ],
        ];
    }
 
    my $user_id = $req->param('id');
    my $img_id = sha1_hex( sprintf '%s::%010.8f', $user_id, Time::HiRes::time );
 
    my $imgdir = $config->{path}{imgdir};
    my $dest = sprintf(
        '%s/%s/%s/%s/%s.png',
        $imgdir,
        ( $img_id =~ /^(.)(.)(.)/ ),
        $img_id,
    );
    my $destdir = dirname $dest;
    make_path $destdir  if ! -d $destdir;
    move $up->path, $dest;
 
    my $url = sprintf '%simg/%s', $config->{base_url}, $img_id;
 
    return [
        200,
        [ 'Content-Type' => 'text/plain' ],
        [ $url ],
    ];
}
 
1;

ユーザの(Gyazo クライアントの?)ID が,パラメータ id で渡ってくるので,これを基に画像管理をするようなしくみがあれば,さらに便利そうですね!

サーバアプリケーションの準備: /img

URL で指定された ID に紐づく画像を表示する部分です.

  • POST メソッド以外は弾く
  • リクエストのパスから,画像 ID を抽出する
  • 画像 ID に紐づく画像ファイルを探す
  • 画像ファイルを open し,そのファイルハンドルをレスポンスに含める

やっていることはこれだけです.

package MyGyazo::Web::Image;
use 5.16.0;
use warnings;
use utf8;
use MyGyazo::Util;
use parent 'Plack::Component';
use Plack::Request;
use File::Basename;
use File::Path qw/make_path/;
 
state $config = config();
 
sub call {
    my ($self, $env) = @_;
    my $req = Plack::Request->new( $env );
 
    if ( $req->method ne 'GET' ) {
        return [
            405,
            [ 'Content-Type' => 'text/plain' ],
            [ 'method not allowed' ],
        ];
    }
 
    my ($img_id) = ( $req->path =~ m!^/(.{40})$! );
    my $file_glob = sprintf(
        '%s/%s/%s/%s/%s.*',
        $config->{path}{imgdir},
        ( $img_id =~ /^(.)(.)(.)/ ),
        $img_id,
    );
    my ($imgfile) = glob $file_glob;
    if ( ! $imgfile ) {
        return [
            404,
            [ 'Content-Type' => 'text/plain' ],
            [ 'not found' ],
        ];
    }
 
    open my $fh, '<', $imgfile  or return [
        500,
        [ 'Content-Type' => 'text/plain' ],
        [ 'cannot open file' ],
    ];
 
    return [
        200,
        [ 'Content-Type' => 'image/png' ],
        $fh,
    ];
}
 
1;

アクセスコントロールとかが拡張のための次のステップですかね(必要であれば,ですが).

サーバアプリケーションの起動

依存モジュールたちは carton で管理しているので,次のようにしてアプリケーションを起動します:

% carton exec -- plackup --port=5999

http://localhost:5999/ へアクセスして,hello が表示されれば問題ないでしょう.

おためし

こんな感じです:

ss-1375406215

わざわざリバースプロキシさせた背景

Windows からスクリーンショットをテストアップロードする場合,先のようにアプリケーションを起動しているので, 最初,そのアップロード先を upload_server={q-chanのIPアドレス}:5999 としていました.readme にも次のようにあります:

<設定ファイルについて>
INI形式です。[gyazowin+] セクション内にすべての設定を記述します。

upload_server=アップロード先サーバ
アップロード先のサーバを設定します。
ドメイン名[:ポート] の形で指定します。
初期設定: gyazo.com

gyazowin+ readme.txt

で,この設定で gyazowin.exe を実行,スクリーンショットを撮ったところ,次のようなエラーに出くわしました:

ss-1375399821

ちなみに,upload_server=...:80 の場合はエラー出ませんでした.

これらのことと,次のあたりを見るに,ポート設定が反映されていない気がしますがどうなんでしょ:

945
946
947
HINTERNET hConnection = InternetConnect(hSession, 
		lpwcUploadServer, INTERNET_DEFAULT_HTTP_PORT,
		lpwcId, lpwcPassword, INTERNET_SERVICE_HTTP, 0, NULL);

ということで,nginx に次のような設定を追加して,plackup しているアプリケーションにリバースプロキシするに至りました:

server {
    listen 80;
    server_name gyazo.q-chan.local;
 
    location / {
        proxy_pass http://localhost:5999/;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

おわりに

以上,グループ内でスクリーンショット共有するための手段として,簡単なオレオレ Gyazo サーバ的なアプリケーションを書いたときのメモでした.

オレオレ Gyazo サーバアプリケーションを Perl(with Plack)で書く場合,最低限満たすべき要件はごく単純ながら,GET メソッド,POST メソッド,ファイルアップロード処理,といった,Web アプリケーションの基本的な要素がそれなりに含まれているため,Plack アプリケーションの入門的な題材として良い感じに適しているのではないかな,などと,書いてて思いました.