ノート 
訪問者数 4990      最終更新 2015-05-21 (木) 16:07:16

単純なCGI (出力のみ)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import codecs
import cgitb
cgitb.enable()

sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
print ('Content-type: text/html; charset=UTF-8')
print("\r\n\r\n")

print(u"あいう")

ポイントは、

  1. 先頭にpythonを実行させるための#!/usr/bin/env pythonを書く
  2. 漢字を使うなら coding: utf-8 を書く
  3. 漢字を使うなら sysとcodecsをimportしておき、utf_8のwriterをstdoutにかましておく
  4. cgitbをimportし、メソッドcgitb.enable()を実行しておく。これによりデバッグ情報がホームページ上に表示される。営業運転時には外す必要あり。
  5. ホームページとして出力するためには、先頭に'Content-type: text/html; charset=UTF-8'を入れる。charsetはブラウザが漢字コードをUTF-8とする指示。
    この直後に必ず空行を1行以上入れる(ヘッダー区別のため)
  6. このファイルをセーブする時、UTF-8コードを指定(に変換)する
    更に、ファイルの実行可能ビットを立てる(chmod 755 ファイル名)

このファイルを、cgi-binディレクトリ下に置けば、呼び出せる。

フォームのページの場合

例 (ここでは、formを書き出すhtml部分も同じプログラム中でprintで出力している)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# coding: utf-8
import sys
import codecs
import cgi
import cgitb
cgitb.enable()

sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
print ('Content-type: text/html; charset=UTF-8')
print ("\r\n\r\n")

form = cgi.FieldStorage()
laptops = form.getvalue('laptops','0')
desktops = form.getvalue('desktops','0')

print """
<html>
<body>
<form action='mytest.py'>
How many laptops do you own
<input type='radio' checked name='laptops' value='0' />0 
<input type='radio' name='laptops' value='1' />1 
<input type='radio' name='laptops' value='2' />2
<p>
How many desktops do you own 
<input type='radio' checked name='desktops' value='0' />0 
<input type='radio' name='desktops' value='1' />1 
<input type='radio' name='desktops' value='2' />2
<p>
<input type='submit' value='送る' />
<p>
You own %d computers.
</form>
</body>
</html>""" % (int(laptops)+int(desktops))

この例では、次のようなことに注目する。

  1. cgiライブラリ中のFieldStorageを使ってフォーム入力用の場所を確保する。getvalueメソッドで実際に値を取り込む。
  2. <html>から</html>までは、print """ ... """ によってまとめて書き出している。
    もちろん、1行ずつprintで書いていってもいいのだろう。
    また、そのprint文中の%dで書かれる値を、print ""..."" % int(laptops)+int(desktops) で計算・指定している。

Python-CGIで複数ファイルを一発でアップロードする

  1. 複数ファイルをアップロードする件(HTML-5にて新規導入の規格)
    ファイルアップロードの際に、ファイル選択画面で複数ファイルを選択し(WindowsならばCTRLキーを押しながらマウス左クリックして、複数ファイルを選択できる)、それを同時にアップロードする。
    HTML-5から導入された新しい規格で、HTML-5に対応するFireFoxなどのみで使うことができる。
    サーバー側CGIは、たとえばapache+PHPやapache+Pythonで使える。多分他の言語でもOKと思うが、未確認(「使い方」=API=上の差異があるはず)。
  2. Pythonの場合、

#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import sys,os,codecs
import cgi
import cgitb
cgitb.enable()

sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
print ('Content-type: text/html; charset=UTF-8')
print ("\r\n\r\n")

html = u'''
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html" charset="UTF-8" />
  <title>ファイルアップロード</title>
</head>
<body>
<form action="upload.py" method="post" enctype="multipart/form-data">
  <input type="file" name="file" multiple/>
  <input type="submit" />
</form>
</body>
</html>
'''

result = ''
form = cgi.FieldStorage()
if form.has_key("file"):
    fileitem = form['file']
    if (isinstance(fileitem, list)):
       # Multi-File
       for i in range(0, len(fileitem)):
          fout = file(os.path.join('/tmp', fileitem[i].filename), 'wb')
          while True:
              chunk = fileitem[i].file.read(1000000)
              if not chunk:
                  break
              fout.write(chunk)
          fout.close()

    else:
       # Single-File
       fout = file(os.path.join('/tmp', fileitem.filename), 'wb')
       while True:
          chunk = fileitem.file.read(1000000)
          if not chunk:
              break
          fout.write(chunk)
       fout.close()

print html + result

ポイントは、 fileitem = form['file'] で返ってくる値が、複数ファイルの時はリストになって、複数のファイルを扱える。

この fileitem が、ファイルを1つだけしかアップロードしない時(リストにならない)と、複数ファイルをアップロードした時(リストになる)で、形が違う。
従って、取り出す時に区別する必要がある。

複数ファイルの時は、 fileitem[i] のように、どのファイルかを区別する。また、len(filename)によって、ファイルの個数を知ることができる。単一ファイルの時にlen(filename)を行うと(単一オブジェクトのlenを取ることになるので)エラーになる。

fileitemの属性は、元のファイルの名前が filename、ファイルの内容が valueである。

SELinuxでの(アップロード用ディレクトリの)書込み許可を出す

SELinux下では、httpdによる書き込みは一般には止められる。(Permission Denied) 特定のディレクトリに対して書き込みを許可するには、

%semanage fcontext -a -t httpd_sys_content_rw_t "/var/www/html/fcs20/FILE"
%semanage fcontext -a -t httpd_sys_content_rw_t "/var/www/html/fcs20/FILE/*"
%restorecon -R -v /var/www/html/fcs20/FILE
restorecon reset /var/www/html/fcs20/FILE context system_u:object_r:httpd_sys_content_t:s0->system_u:object_r:httpd_sys_content_rw_t:s0

この辺も参照

上記のSELinuxでの書込み許可が出た上で、スクリプトからアップロード用ディレクトリへファイルを書く (このファイルの名前をmultifile2.pyとする)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# coding: utf-8
import sys
import codecs
import cgi
import cgitb
cgitb.enable()

sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
print ('Content-type: text/html; charset=UTF-8')
print ("\r\n\r\n")

form = cgi.FieldStorage()
print("タイプは" + str(form.type) + "<br>")
if (form.type != "application/x-www-form-urlencoded"):
  fileitem = form['userfile[]']

  if (isinstance(fileitem, list)):
    # Multiple files
    print("  複数ファイルです<br>")
    print("  ファイル数は" + str(len(fileitem)) + "<br>")
    for i in range(0, len(fileitem)):
      print(str(fileitem[i].filename) + "<br>")
      fp = open("/var/www/html/fcs20/FILE/"+str(fileitem[i].filename), "w+")
      fp.write(fileitem[i].value)
  else:
    print ("  単数ファイルです<br>")
    print(str(fileitem.filename) + "<br>")
    fp = open("/var/www/html/fcs20/FILE/"+str(fileitem.filename), "w+")
    fp.write(fileitem.value)

print"""
<html>
<body>
<form action="multifile2.py" method="post" enctype="multipart/form-data">
  Send these files:<br />
  <input type="hidden" name="MAX_FILE_SIZE" value="300000" />
  <input name="userfile[]" type="file" multiple/><br />
  <input type="submit" value="Send files" />
</form>
</body>
</html>
"""

もう少し複雑なページを考える。2つsubmitボタンがある場合

気をつけるのは、同じnameの下に複数の値が返される時、リストで返されるが、単一の時はリストでないので、その区別をすること。⇒ 参考 マニュアル

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# coding: utf-8
import sys
import codecs
import os
import cgi
import cgitb
cgitb.enable()

sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
print ('Content-type: text/html; charset=UTF-8')
print ("\r\n\r\n")

datadir = "/var/www/html/fcs20/FILE/"
form = cgi.FieldStorage()
print("nameは" + str(form.name) + "<br>")
print("valueは" + str(form.getvalue("submit")) + "<br>")
print("タイプは" + str(form.type) + "<br>")

if (form.getvalue("submit") == "消去"):
  print(str(form) + "<br>")
  if ("fnum" in form) :
    selectitem = form['fnum']
    print(str(selectitem) + "<br>")
    if (isinstance(selectitem,list)) :
      for i in range(0, len(selectitem)):
        print(str(selectitem[i].value))
    else:
      print(str(selectitem.value))
  else:
    print('no checks')

else:
  if (form.type == "multipart/form-data"):
    fileitem = form['userfile[]']

    if (isinstance(fileitem, list)):
      # Multiple files
      print("  複数ファイルです<br>")
      print("  ファイル数は" + str(len(fileitem)) + "<br>")
      for i in range(0, len(fileitem)):
        print(str(fileitem[i].filename) + "<br>")
        #print(fileitem[i].value)
        #print("<br>=======================<br>")
        fp = open(datadir+str(fileitem[i].filename), "w+")
        # fp = open("/tmp/"+str(fileitem[i].filename), "r+")
        fp.write(fileitem[i].value)
    else:
      print ("  単数ファイルです<br>")
      print(str(fileitem.filename) + "<br>")
      #print(fileitem.value)
      fp = open(datadir+str(fileitem.filename), "w+")
      # fp = open("/tmp/"+str(fileitem.filename), "r+")
      fp.write(fileitem.value)
 
print"""
<html>
<head>
<META http-equiv="Content-Type" content="text/html;charset=utf-8">
<title> FCS20 </title>
</head>
<body>
<table>
<tr><td width=300>
"""

i = 0
print("作業フォルダ内のファイル一覧<br>")
print('<form action="fcs20.py" method="post" enctype="multipart/form-data">')
print("<table><tr><td>")
for f in (os.listdir(datadir)):
  print('<input type="checkbox" name=fnum value=' + str(i) +"> " + str(f) +  "</td></tr><tr><td>")
  i=i+1
print("</tr></td></table>")
print('<input type="submit" name="submit" value="消去" />')
print("</form>")

print"""
</td><td>
追加ファイルの転送<br>
<form action="fcs20.py" method="post" enctype="multipart/form-data">
  <input type="hidden" name="MAX_FILE_SIZE" value="30000000" />
  <input width=300 name="userfile[]" type="file" multiple/><br />
  <input type="submit" name="submit" value="転送" />
</form>
</td></tr></table>
</body>
</html>
"""

SELinuxで、public_htmlディレクトリの読み出しアクセス許可を出す

SELinuxで、public_html/cgi-binディレクトリ以下のスクリプトでネットアクセスを許す

なお、SELinuxのいろいろな許可ビットの一覧を見るには、


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2015-05-21 (木) 16:07:16 (793d)