質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.60%

HTMLでアップロードされたファイルを保存するCGIスクリプトを作成するにはどうすればいいでしょうか

解決済

回答 1

投稿

  • 評価 2
  • クリップ 2
  • VIEW 9,598

score 46

以下のサイトを参考に、HTMLからアップロードされたfileを受け取り保存するCGIスクリプトをpythonで作成したいと考えております。
http://www.gesource.jp/programming/python/cgi/0115.html

しかしながらいざ実行してみると、HTMLはちゃんと作成できファイル送信フォームも生成できたのですが、送信すると以下のエラーが出てしまいます。

Error response
Error code: 501
Message: Can only POST to CGI scripts.
Error code explanation: 501 - Server does not support this operation.

test15.html

<html>
<head>
  <meta http-equiv="Content-Type" content="text/html" charset="UTF-8" />
  <title>ファイルをアップロードする</title>
</head>
<body>
<h1>ファイルをアップロードする</h1>
<p>%s</p>
<form action="test15.cgi" method="post" enctype="multipart/form-data">
  <input type="file" name="file" />
<input type="submit" />
</form>
</body>
</html>

test15.cgi

import cgi
import os, sys

try:
    import msvcrt
    msvcrt.setmode(0, os.O_BINARY)
    msvcrt.setmode(1, os.O_BINARY)
except ImportError:
    pass

result = ''
form = cgi.FieldStorage()
if form.has_key('file'):
    item = form['file']
    if item.file:
        fout = file(os.path.join('/tmp', item.filename), 'wb')
        while True:
            chunk = item.file.read(1000000)
            if not chunk:
                break
            fout.write(chunk)
        fout.close()
        result = 'アップロードしました。'

print html % result
print("upload is done")

当方、CGIについて最近勉強したばかりで筋違いな質問であれば大変申し訳ないです。
HTMLで送信したファイルがtmpフォルダに保存されるようなCGIを作りたいと考えております。
何卒、よろしくお願いいたします。

  • クリップを取り消します

  • 質問の評価を上げたことを取り消します

  • 質問の評価を下げたことを取り消します

回答 1

checkベストアンサー

+3

こんにちは。

サーバーがステータス501を返しているので、cgiスクリプトが正しく動作して
いないようです。ご提示のコードですと、何点か問題ありそうでした。

  1. 1行目でシバン(Shebang)にてスクリプトが指定されていないので、
    pythonスクリプトとして動作できない。
  2. 日本語部分があるので、文字コードを指定しないと文字化けの恐れがある。
  3. HTTPレスポンスとして返す時のContent-Type が指定されていない。
  4. 'html' 変数に初期値が設定(宣言)されていない。未定義変数エラー。

プログラムを造ってサーバーに設置したときは、cgiとしてサーバーに
実行させる前に端末上で文法チェックしてみると良いです。

以下で簡単に文法チェックすることができます。
(もちろん、実行時のエラーはこれでは分かりません)

$ python -m py_compile test15.cgi

ご提示のコードですが、以下のように直すことで動作しました。
確認用のコードを残していますので、差分を取って比較してみてください。
尚、文字コードはutf-8で保存してください。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import cgi
import os, sys

try:
    import msvcrt
    msvcrt.setmode(0, os.O_BINARY)
    msvcrt.setmode(1, os.O_BINARY)
except ImportError:
    pass

result = 'DUMMY'
form = cgi.FieldStorage()
if form.has_key('file'):
    item = form['file']
    if item.file:
        fout = file(os.path.join('/tmp', item.filename), 'wb')
        while True:
            chunk = item.file.read(1000000)
            if not chunk:
                break
            fout.write(chunk)
        fout.close()
        #result = 'upload is done'
        result = 'アップロード'

#print html % result

print "Content-Type: text/html"
print
html = "<html><body>%s</body><html>"
print html % result

python3に関して追記

python3 でのご質問だったのかもしれませんので、すみませんが以下、追記させていただきます。

参考にされたサイト様のサンプルプロはpython2 のものです。
私が提示したコードもpython2 のものなので、サーバー上でpython3でcgiを
稼動させる場合、文法エラーで動作しません。
コードをpython3でほぼ等価なものに書き直し、動作確認したものを以下に示します。
※「アップロード」の部分はそのままだとエラーになり、
コードが煩雑になるのでASCII文字列に直させてもらいました。

今後pythonでcgiを造られる場合は、バージョンの違いに注意する必要があるかとは思います。

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import cgi
import os, sys

try:
  import msvcrt
  msvcrt.setmode(0, os.O_BINARY)
  msvcrt.setmode(1, os.O_BINARY)
except ImportError:
  pass

result = 'DUMMY'
form = cgi.FieldStorage()
#if form.has_key('file'):
if 'file' in form:
  item = form['file']
  if item.file:
#    fout = file(os.path.join('/tmp', item.filename), 'wb')
    fout = open(os.path.join('/tmp', item.filename), 'wb')
    while True:
        chunk = item.file.read(1000000)
        if not chunk:
          break

        fout.write(chunk)
        fout.close()
        result = 'upload is done'
        #result = "アップロード"

print("Content-Type: text/html")
print("")
html = "<html><body>{0}</body><html>"
print(html.format(result))

投稿

編集

dodox86

score 8775

  • dodox86

    dodox86

    2017/10/01 18:26

    進展があり、的外れではなかったようで安心しました。今回のケースのデバッグですが、スクリプト中で標準エラー出力にprintすることでアクセスログの方に出ます。例えば
    print("STEP#1: ", file=sys.stderr)
    と記述すると、アクセスログには
    127.0.0.1 - - [01/Oct/2017 18:16:53] "POST /cgi-bin/test17.cgi HTTP/1.1" 200 -
    STEP#1:
    のように出ますので、デバッガーを用意するのが大変な場合は代替策のひとつになります。また、Windows環境では '/tmp'の代わりに TEMP環境変数で出力されるディレクトリが書き込み可能です。
    書き込み可能なディレクトリを新設するのが難しい場合、コンソール(コマンドプロンプト)上で
    C> echo %TEMP%
    で表示されたディレクトリを'/tmp'の代わりに利用することもできます。ご参考まで。
  • dodox86

    dodox86

    2017/10/01 18:31

    %TEMP%で示されるディレクトリは他のWindowsアプリも使用しているので、既にたくさんのファイルがあるかもしれません。競合しないようファイル名に注意してください。その下に更に専用のディレクトリを作って、そこに保存する方法がおすすめです。
  • mmss

    mmss

    2017/10/01 18:39

    ご丁寧にアドバイスまでいただき恐縮です。

    書き込み可能、のところも少々疑心暗鬼でしたので、ぜひご教授いただいたアドバイスを利用させていただきます!!何から何までご助力いただき、誠にありがとうございました。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.60%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る