OZの部屋

なんでも日記です

prototype.js + ruby + rmagick でファイルアップロード No3

2011-03-09 16:53:33 | プログラム
---------rubyソース upload.cgi -------------
#!/usr/local/bin/ruby -Ku
# ファイルアップロードのためのCGI

# javascript ozUpload.js, ozUpload.css と共に使う
#  prototype.js も必要 (1.6以上)
# 必要な追加ライブラリ RMagick,json
# 対応 画像フォーマット Jpeg,GIF,PNG

require "rubygems"
require 'RMagick'
include Magick
require "json"
require "cgi"

######################################################
# 以下の3つの保存ディレクトリを指定
# アップロード経過(%) を保存するディレクトリ 
#この中にマジックナンバーの名前でファイルを作る
$tempdir='/tmp/uploadcgi'
#画像保存ディレクトリ Webサーバーが書き込み権限を有すること
#  サーバーディレクトリ
$imgdir='/var/www/html/img-post/001/'
# $imgdir の ウエブディレクトリ
$wwwimgdir='/img-post/001/'
######################################################

#置き換えのための文字列
$responsJson='%==responsJson==%'
$uploadIdSub='%==uploadIdSub==%'

# 画像アップ時 返すHTML
# このHTMLは親ウインドウ内のiframeにロードされる。
# HTMLロード後親ウインドウのアップロード完了後ファンクションを呼び出す。
$resHTML= <<EOL
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script>

function addEvent(obj, evType, fn){
    if (obj.addEventListener){obj.addEventListener(evType, fn, true)}
    if (obj.attachEvent){ obj.attachEvent("on"+evType, fn)}
}
function init(){
window.parent.temp_oz_json=#{$responsJson};
eval('window.parent.oz_firstfunc(window.parent.temp_oz_json)');
eval('window.parent.ozUploadhash.#{$uploadIdSub}.postfunc(window.parent.temp_oz_json)');
eval('window.parent.ozUploadhash.#{$uploadIdSub}.afterfunc(window.parent.temp_oz_json)');
}
addEvent(window,'load',init);

</script>
</head>
<body>
</body>
</html>
EOL

##########################################################
#ajaxとして出力する
##########################################################
#戻り値なし
#  body: ajaxとして出力する内容
def ajaxout(body)
    CGI.new.out(
        {"type"=>"text/javascript","charset"=>"UTF-8","language"=>"ja"}
    ){
        "\xef\xbb\xbf"+body
    }
end

##########################################################
#jsonデータとして出力する
##########################################################
#戻り値なし
#  hash: jsonデータとして出力するhashデータ
def jsonout(hash)
    CGI.new.out(
        {"type"=>"text/javascript+json","charset"=>"UTF-8","language"=>"ja"}
    ){
        "(%s)"%[hash.to_json]
    }
end

##########################################################################
#メインクラス
#  javascriptでこのCGIを実行するには
#  1. 同期Ajax URLパラメータで cmd=getmagick でマジックナンバーを取得
#  2. アップロードフォームsubmit()
#       enctype='multipart/form-data' method='post' target='body内のiframe'
#     
#
##########################################################################
class OzMain
    #######################################################
    # オブジェクト初期化
    # CGIパラメータcmdを解析して処理を振り分け
    #######################################################
    def initialize
        @cgi = CGI.new
        @cmd = "#{@cgi['cmd'].to_a.first}"
        @cmd = 'start' if @cmd.empty?
        __send__("cmd_#{@cmd}")
    end #def initialize
    
    #######################################################
    # 実行本体
    # CGIパラメータcmdを解析して処理を振り分け
    #######################################################
    def cmd_start
        hash={}

        #アップロードファイルサイズ最大バイト数
        max_file_size= @cgi.include?('max_file_size') ? @cgi['max_file_size'].read.to_i : 10000
        max_file_size*=1000
        #画像がURLエンコードされたサイズ、さらに他のパラメータのデータサイズもあるので
        #かなり大雑把だけれども content_lengthから1000引いた数値をファイルサイズと仮定する
        data_length=@cgi.content_length -1000 
        hash[:stat]=false
        hash[:img_format]=@cgi['img_format'].read
        if !imgformatcheck(@cgi.params['oz_file1'][0].content_type,hash[:img_format]) then
            hash[:message]="アップロード可能なファイルは"+hash[:img_format]
        elsif max_file_size<data_length then
            hash[:message]="ファイルサイズが大きすぎます(サイズ誤差あり) >#{(max_file_size/1000).to_s}Kbytes"
        else
            begin
                hash[:sessionkey]=@cgi['magickno'].read
                fpath=$tempdir+'/'+hash[:sessionkey]
                img=readimage(data_length,fpath)
                saveimage(img,hash[:sessionkey],hash)
                hash[:stat]=true
            rescue
                hash[:message]="システムの不具合で保存できませんでした。"
            end
        end
        hash[:destform]=@cgi['destform'].read
        hash[:uploadid]=@cgi['uploadid'].read
        hash[:no_output2]=((@cgi['no_output2'].read).to_s.match(/true/i)!=nil)
        outHTML(hash)
    end
    
    # imgformatの中にtargetが示す画像フォーマットが含まれるか調べる
    # contenttype に入っているのは image/pjpeg,image/gif, image/x-png など
    #  含まれるとき 数値 含まれないとき nil
    def imgformatcheck(contenttype,imgformat)
        imgformat.upcase.sub('JPG','JPEG').gsub(',',' ').split.each{|e| break if contenttype=~/#{e}/i}
        $&
    end
    
    def mksessionfile(fpath)
        Dir.mkdir($tempdir) if !FileTest.exist?($tempdir)
        File.open(fpath,'w'){|f| f.puts 0}
    end
    
    #画像データを読み込み シリアル番号名のファイル(fpath)に読み込み残り割合を
    #百分率整数で保存 読み込み終了時にシリアル番号名のファイルは削除する
    def readimage(data_length,fpath)
        xdata=[]
        data=@cgi.params['oz_file1'][0]
        remlen=data_length
        bufsize=1024 #1回で読み込むサイズ
        while !data.eof
            xdata<<data.read(bufsize)
            File.open(fpath,'w'){|f|
                f.puts(100-(remlen.to_f/data_length*100).to_f)
            }
            remlen-=bufsize
            sleep 0.0001      #プログレスバーが見えないので
        end
        File.delete(fpath)
        xdata.join
    end
    
    # サムネイルと元画像(ただし制限より大きいときは縮小したもの)をファイル保存する
    def saveimage(data,fserial,hash)
        #サムネイルのサイズ thumbnail_sizeにはいる様に作成する
        thumbnail_size=@cgi.include?('thumbnail_size') ? @cgi['thumbnail_size'].read.to_i : 100
        #画像の一辺最大サイズ このサイズより大きい場合はこのサイズに縮小する
        max_image_size=@cgi.include?('max_image_size') ? @cgi['max_image_size'].read.to_i : 4000

        #アニメーションGIFに対応するためImageListに読み込む
        imgs=Magick::ImageList.new
        #Imageオブジェクトに画像データを読み込み
        imgs.from_blob(data)
        img=imgs.first
        imgcols=img.columns
        imgrows=img.rows
        hash[:shrink]=false
        #画像があまりにも大きいときmax_image_sizeに入りきるサイズまで縮小する
        if img.columns>max_image_size or img.rows>max_image_size then
            #サイズ最大制限 ジオメトリは '9999x9999'のように文字列
            sizex='%dx%d'%[max_image_size,max_image_size]
            #ジオメトリにあわせて(指定サイズに入るように)画像をリサイズ
            imgs.each{|img|
                img.change_geometry(sizex){|cols, rows, img2| 
                    #img2 の実体はimg なのでimg自体が縮小される
                    img2.resize!(cols, rows)
                    imgcols=cols
                    imgrows=rows
                }
            }
            hash[:shrink]=true
        end
        #保存画像のサイズをhashに入れる
        hash[:imgwidth]=img.columns
        hash[:imgheight]=img.rows
        # jpeg,png,gif 形式を取得して小文字へ
        ext=img.format.downcase
        ext='jpg' if ext=='jpeg'
        #大きい画像を保存
        hash[:imgpath]=$wwwimgdir+fserial+'.'+ext
        imgs.write($imgdir+fserial+'.'+ext)
        
        hash[:redundant]=true
        # サムネイル アニメーションGIFのときは最初の1枚のみにする
        if img.columns>thumbnail_size or img.rows>thumbnail_size then
            #サイズ最大制限 
            sizex='%dx%d'%[thumbnail_size,thumbnail_size]
            #ジオメトリにあわせて(指定サイズに入るように)画像をリサイズ
            img.change_geometry(sizex){|cols, rows, img2|
                img2.resize!(cols, rows)
            }
            hash[:redundant]=false
        end
        #サムネイルのサイズをhashに入れる
        hash[:thumbwidth]=img.columns
        hash[:thumbheight]=img.rows
        #縮小画像を保存
        hash[:thumbpath]=$wwwimgdir+fserial+'-mini.'+ext
        img.write($imgdir+fserial+'-mini.'+ext)
    end
    
    #画像アップロードの結果を返す。
    #bodyに結果HashをURIエスケープして返す。返した内容でOnloadイベントで
    def outHTML(hash)
        hash[:outputid1]=@cgi['oz_output1'].read
        hash[:outputid2]=@cgi['oz_output2'].read
        $resHTML.gsub!($uploadIdSub,hash[:uploadid])
        $resHTML.sub!($responsJson,hash.to_json)
        @cgi.out('text/html'){$resHTML}
    end
    
    #アップロードする前にこのパラメータで呼ばれて、セッションIDのようなマジックナンバーを返す。
    def cmd_getmagick
        magickno='%d%4.4d'%[Time.now.to_i,rand(10000)] #少しでも複雑にするため
        mksessionfile($tempdir+'/'+magickno)
        ajaxout(magickno)
    end
    # プログレスバー このパラメータでアップロードが完了するまで何度も呼ばれる
    # このHashはURIエスケープしない
    def cmd_progressbar
        fpath=$tempdir+'/'+@cgi['magickno']
        hash={:stat=>false}
        begin
            File.open(fpath,'r+'){|f| 
                hash[:progress]=f.read.to_s.to_f
            }
            hash[:stat]=true
        rescue
        end
        jsonout(hash)
    end
end

OzMain.new
------------ upload.cgi おわり ----------


最新の画像もっと見る

3 コメント

コメント日が  古い順  |   新しい順
hervelegerdress@outlook.com (herve leger dress)
2020-02-24 23:23:20
https://legerdresses.hateblo.jp/entry/2020/01/28/224736
https://is.gd/BebEb2
https://is.gd/iC9tnd
https://is.gd/zu0Osm
http://bit.ly/2RUM6jI
http://bit.ly/2U5A3Tu
https://legerdresses.hateblo.jp/entry/2020/01/28/225622
https://legerdresses.hateblo.jp/entry/2020/01/28/230359
http://bit.ly/3129DDs
http://bit.ly/2tQQFnn
https://tinyurl.com/vsfxlhu
https://tinyurl.com/rclrs2p
https://tinyurl.com/yx6foctn
https://5e2e91edd225a.site123.me/blog/Herve-leger-story
https://is.gd/5kDxkC
https://is.gd/ntmnfL
https://5e2e91edd225a.site123.me/blog/herve-leger-history
https://5e2e91edd225a.site123.me/blog/herve-leger-things
https://tinyurl.com/vkpjf4s
https://tinyurl.com/vc4t2t3
http://tiny.cc/73wbjz
http://tiny.cc/05wbjz
http://tiny.cc/57wbjz
http://bit.do/fvjG3
http://tiny.cc/d8wbjz
https://ameblo.jp/legerdresses/entry-12570454577.html
https://is.gd/5bVDky
https://is.gd/fAKquP
https://is.gd/jKKd58
http://bit.ly/2t7C3zv
http://bit.ly/38LL4NN
http://bit.ly/2tbhfXS
https://tinyurl.com/teadob9
https://tinyurl.com/u2s9wkl
http://tiny.cc/y8wbjz
http://tiny.cc/o9wbjz
http://tiny.cc/p9wbjz
返信する
Online Canada Giveaways ( Canada Giveaways)
2020-11-12 00:23:10
Nice post.
https://canada-sweepstakes.com/canada-giveaway/
返信する
Orologi Replica (Orologi Replica)
2021-11-29 15:36:57
https://ko-fi.com/post/QUAL-E-IL-ROLEX-MENO-COSTOSO-S6S76X2FJ
https://is.gd/Eu9ic1
https://is.gd/hKu69P
https://is.gd/dpkwi7
https://is.gd/5xB20w
https://is.gd/WzRnwP
https://is.gd/BAM1vp
https://ko-fi.com/post/ROLEX-HISTORY-ONE-X8X06X2L8
https://bit.ly/3FoWJma
https://bit.ly/3Ch4RCZ
https://bit.ly/3CnV91K
https://bit.ly/3cmc0ro
https://bit.ly/3ozRCIW
https://bit.ly/3kGsVJO
https://ello.co/orologimeccanici/loves
https://tinyurl.com/fhwj5uka
https://tinyurl.com/ypbb6fhd
https://tinyurl.com/majd38dp
https://tinyurl.com/9d8sn82y
https://tinyurl.com/t2x78use
https://tinyurl.com/ybz8k7v9
https://ello.co/orologimeccanici/post/bljscg7ry_5oo3b0yprt_w
http://tiny.cc/pzpluz
http://tiny.cc/qzpluz
http://tiny.cc/rzpluz
http://tiny.cc/szpluz
http://tiny.cc/tzpluz
http://tiny.cc/uzpluz
https://ello.co/orologimeccanici/post/n9zpc6cv8mqjym9iu3ucxg
https://padlet.com/orologimeccanici/il_rolex_submariner
https://rebrand.ly/xmrrmk6
https://rebrand.ly/98tiqom
https://rebrand.ly/9c9c31
https://rebrand.ly/r4tz7wi
https://rebrand.ly/kgl37pw
https://rebrand.ly/68tvmev
返信する