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

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

ただいまの
回答率

90.35%

  • Python 3.x

    7394questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

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

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 2,289

mmss

score 32

以下のサイトを参考に、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を作りたいと考えております。
何卒、よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

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

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

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

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 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))

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/01 12:38 編集

    早速ご回答いただき、たいへんありがとうございます。
    大変恐縮ですが、ご教授いただいたCODEで修正しても依然として同じエラーが出てしまいましたので、再度ご助力いただけますと幸いです。

    ご教授いただいた文法チェックのCODEを試してみたところ、とくに問題なさそうでしたので恐らくCODE以前の問題ではないかと考えております。
    C:\server>python -m py_compile test15.cgi

    例えば、フォルダ構成が以下のようになっておりますが、特に問題ございませんでしょうか。
    server
    -- test15.html
    -- test15.cgi
    -- cgi-bin -- test15.cgi , tmp
    ※test15.cgiはどこに置けば起動するかわからなかったため、同じものを2つserverとcgi-binに置いております。

    また、サーバーに関してはコマンドプロンプトに以下のCODEを入力して構築しております。
    C:\server>python -m http.server --cgi
    Serving HTTP on 0.0.0.0 port 8000 ...
    127.0.0.1 - - [01/Oct/2017 12:28:41] "GET / HTTP/1.1" 200 -
    127.0.0.1 - - [01/Oct/2017 12:28:43] "GET /test15.html HTTP/1.1" 200 -
    127.0.0.1 - - [01/Oct/2017 12:28:49] code 501, message Can only POST to CGI scripts
    127.0.0.1 - - [01/Oct/2017 12:28:49] "POST /test15.cgi HTTP/1.1" 501 -

    何卒、よろしくお願いいたします。

    キャンセル

  • 2017/10/01 12:43

    コメントが僅差ですれ違いました。python のバージョンをご確認ください。python3ですと、前のコードは動作しません。

    キャンセル

  • 2017/10/01 12:48

    Windowsをお使いでしたか。でしたら、tmp ディレクトリの位置も問題です。
    os.path.join('/tmp' の部分で、'/tmp'では無く、Windowsでの書き込み可能ディレクトリPATHを指定してください。

    キャンセル

  • 2017/10/01 13:22

    大変ご丁寧な返答、誠にありがとうございます。
    大変恐縮ですが、書き込み可能なディレクトリ作成方法を色々調べておりますので、少々お待ちいただければと思います。

    キャンセル

  • 2017/10/01 16:00

    書き込み可能なwriteディレクトリを作成し、以下のように変更してみたのですがまたもや同じエラーでした。。。
    fout = open(os.path.join('C:\server\cgi-bin\write', item.filename), 'wb')

    フォルダの属性を表示させるとD(ディレクトリ属性)A(アーカイブ属性)となっておりR(読み出し属性、書き込み禁止)がなかったため書き込みはできるものと思われますが、他に気になる点とうございませんでしょうか。

    キャンセル

  • 2017/10/01 16:34

    こちらのcygwin環境下でも問題が再現しました。
    "POST /test15.cgi HTTP/1.1" 501 -" の部分のアクセスログを見落としていましたが、
    cgi-bin/の下のtest15.cgi を動かす必要があります。test15.html を直してください。
    具体的には<form action="test15.cgi" の部分を、"cgi-bin/test15.cgi" にします。
    cgi-bin/下でないと、cgi用のハンドラーを動かさないようになっていると思われます。

    キャンセル

  • 2017/10/01 17:56

    度重なりご丁寧な対応、ありがとうございます。

    ご教授の通りにaction=""の点を修正したところ今までのエラーが出なくなりました!!
    本当にありがとうございます。

    とはいえ、どうやら私の利用しているpythonが32bitであるせいか、以下のエラーが出てしまいまだ上手くはいきませんが、、、
    OSError: [WinError 193] %1 は有効な Win32 アプリケーションではありません。

    とはいえ、CODE上の問題はすべて上手くいきそうですので、あとは自身の開発環境を組みなおして頑張ってみたいと思います!!本当にありがとうございました!!

    キャンセル

  • 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'の代わりに利用することもできます。ご参考まで。

    キャンセル

  • 2017/10/01 18:31

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

    キャンセル

  • 2017/10/01 18:39

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

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

    キャンセル

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

  • Python 3.x

    7394questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。