PythonのCGIのwavファイルの読み込みに関してです。

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,792

riekosuzuki

score 9

python初心者(python3.5)です。CGIの勉強をしていましてwavファイルの読み込みの方法がどうしてもわかりません。どなたかpythonに詳しい方、ご教示をお願いいたします。
例えば、wavファイルを選択して、そのwavファイルの長さを返したい場合、以下のようにコードを書いたのですが、
実装中に以下のエラーメッセージが発生しました。

発生している問題・エラーメッセージ

A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred.

 /Users/rieko/rieko_data/cgi-bin/test1.py in ()
     22 form = cgi.FieldStorage()
     23 wf = form['file']
=>   24 length = float(wf.getnframes()) / wf.getframerate()  
     25 content = length
     26 
length undefined, builtin float = <class 'float'>, wf = FieldStorage('file', 'call.WAV', b'RIFF$>u\x01WA...x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'), wf.getnframes undefined, wf.getframerate undefined
 /Users/rieko/.pyenv/versions/3.5.3/lib/python3.5/cgi.py in __getattr__(self=FieldStorage('file', 'call.WAV', b'RIFF$>u\x01WA...x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'), name='getnframes')
    583     def __getattr__(self, name):
    584         if name != 'value':
=>  585             raise AttributeError(name)
    586         if self.file:
    587             self.file.seek(0)
builtin AttributeError = <class 'AttributeError'>, name = 'getnframes'
AttributeError: getnframes 
      args = ('getnframes',) 
      with_traceback = <built-in method with_traceback of AttributeError object>

該当のソースコード

import wave
from pylab import *
import sys
from pydub import AudioSegment
import cgi
import cgitb

cgitb.enable()

html_body = u"""
<html>
<head>
<meta http-equiv="content-type"content="text/html\n"></head>
<body>
%s
</body>
</html>"""

form = cgi.FieldStorage()
wf = form['file']
length = float(wf.getnframes()) / wf.getframerate()  # 波形長さ(秒)
content = length


print ("Content-type: text/html;charset=utf-8\n")
print ((html_body % content))


<<HTMLファイル>>

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8"> </head>
<body>
  <form action="/cgi-bin/test1.py" method="post" enctype="multipart/form-data"> 入力してください。<br /> 必須 :
    <input type="file" name="file" />
    <button>Send the file</button>

  </form>
</body>
</html>

試したこと

以下のように、textファイルを選択する場合はうまくいきます。

import cgi
import sys
import cgitb

cgitb.enable()

html_body = u"""
<html>
<head>
<meta http-equiv="content-type"content="text/html\n"></head>
<body>
    %s
</body>
</html>"""

form=cgi.FieldStorage()
fileitem = form["file"]
kek =[]
if fileitem.file:

    linecount = 0
    while True:
        line = fileitem.file.readline()
        kek.append(line)
        if not line: break
        linecount = linecount + 1
    content = kek
print ("Content-type: text/html;charset=utf-8\n")
print ((html_body % content))

補足情報(言語/FW/ツール等のバージョンなど)

より詳細な情報

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • KSwordOfHaste

    2017/04/29 12:44 編集

    コードタグが少々乱れています。```python<改行>プログラムの中身・・・<改行>```と書いてみてください。
    ---
    まだ乱れてますね。プレビュー画面をよくみて、コード範囲が意図通りに表示されることをご確認ください。

    キャンセル

回答 2

checkベストアンサー

+1

御質問に提示頂いている元のソースコードで、 wf = form['file'] となっているところを次のように書き換えてみると如何でしょう。一応、手元では動作しているようです。

wf = wave.open(form['file'].file, mode='rb')

KSwordOfHaste さんもご指摘の通り、 cgi.FieldStorage で受け取ったファイルはただのファイルでありバイト列であり、 WAVE ファイル用の機能を有したオブジェクトではありません。そのため、 getnframes() や getframerate() といった関数を使いたければ、 import している wave モジュールから wave.open() を呼び出してファイルを解析させ、 wave.Wave_read オブジェクトを得る必要があります。

幸い、 wave.open() はファイル名だけでなく、 File-like オブジェクト (ファイルを単純に open() したときに得られるような、 read() や write() を実装したオブジェクト) を引数にとれるので、 cgi.FieldStorage オブジェクトの file プロパティを渡してやれば済むようです。ただ、元の File-like オブジェクトの mode を引き継ごうとして失敗するので、 mode='rb' も明示する必要がありました。このあたり、詳しくは「ファイルオブジェクト」「File-like object (ファイル類似オブジェクト、とも)」などで検索してみると良いかもしれません。

尚、こういった標準ライブラリの使い方については、 CGI 経由でなく、一度インタラクティブシェル (python とだけ入力してた時に立ち上がる対話環境) か、当該機能だけを記述した適当なスクリプトファイルを使って試してみると、動作が分かりやすく学びやすいと思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/05/05 00:50

    本当にありがとうございます。
    次のようにコードを書き直しましたが、
    エラーが出てしまいます。
    アドバイスいただきましたように、一度CGI 経由でなく、インタラクティブシェル で基本から学んでみます。
    <試したコード>
    from pylab import *
    import codecs
    import os
    from pydub import AudioSegment
    import cgi
    import sys
    import cgitb

    cgitb.enable()

    html_body = u"""
    <html>
    <head>
    <meta http-equiv="content-type"content="text/html\n"></head>
    <body>


    <H1>%s</H1>
    </body>
    </html>"""
    form = cgi.FieldStorage()
    wf = wave.open(form['file'].file, mode='rb')
    # save file
    fout = file("tmp/tmp0000.wav", "wb")
    while True:
    chunk = wf.read(1024 * 1024 * 10)
    if not chunk:break
    fout.write(chunk)
    wf = wave.open('tmp/tmp0000.wav',"rb")
    length = float(fout.getnframes()) / fout.getframerate() # 波形長さ(秒
    content = length
    fout.close

    print ("Content-type: text/html;charset=utf-8\n")
    print ((html_body % content))
    <エラーメッセージ>
    A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred.

    /Users/rieko/rieko_data/cgi-bin/test1.py in ()
    27 # save file
    28 #fout = wave.open("tmp/tmp0000.wav", "wb")
    => 29 fout = file("tmp/tmp0000.wav", "wb")
    30 while True:
    31 chunk = wf.read(1024 * 1024 * 10)
    fout undefined, file undefined
    NameError: name 'file' is not defined
    args = ("name 'file' is not defined",)
    with_traceback = <built-in method with_traceback of NameError object>

    キャンセル

  • 2017/05/05 01:15

    質問本文の「該当のソースコード」にお書きになっている、いろいろ変に弄る前のソースコードを修正してみてください。

    キャンセル

  • 2017/05/07 12:59

    本当に本当にありがとうございます!!いろいろ変に弄る前のソースコードを修正(wf = wave.open(form['file'].file, mode='rb'))したら、成功しました。
    感謝いたします。

    キャンセル

0

クライアントからアップロードされたWavファイルをCGIで解析する処理ですが、cgiモジュールで取得できるファイルデータform['file']は単なるバイナリーデータの羅列でそれをwaveデータとしては扱えません。一旦サーバー上にWAVファイルとしてセーブしてから改めてwaveモジュールのopenメソッドを使ってwaveオブジェクトを生成しないといけないようですね。

ここのサンプルに、cgiでフォームから取り出したバイナリーデータをWAVファイルとしてセーブするサンプルがあります。

上記を参考に、一旦サーバー上の一時ファイルへWAVファイルを格納し、その後waveオブジェクトを生成し、フレームレートやフレーム数を取り出すといった部分のみコードを書いてみました。

...
waveFile = form['file']
# save file
fout = file("tmp/tmp0000.wav", "wb")
while True:
  chunk = waveFile.file.read(1024 * 1024 * 10)
  if not chunk:
    break
  fout.write(chunk)
fout.close()
//これでサーバーの'tmp/tmp0000.wav'にWAVファイルができている
wf = wave.open('tmp/tmp0000.wav')
length = float(wf.getnframes()) / wf.getframerate()  # 波形長さ(秒
...


上記はformにfileが指定されていなかった場合のエラーチェックなどは省略しています。CGIをテストできる環境がないため間違いなどありましたらご容赦ください。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/04/29 19:40 編集

    本当にありがとうございます。
    コードを試してみたのですが、以下のようなエラーがでてしまいました。
    サーバーはcgiserver.pyでたてています。
    cgi-bin-------test1.py
    |
    cgiserver.py
    test1.html

    レンタルサーバーにする必要があるのでしょうか?

    <エラー表示>
    A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred.

    /Users/rieko/rieko_data/cgi-bin/test1.py in ()
    31 waveFile = form['file']
    32 # save file
    => 33 fout = file("tmp/tmp0000.wav", "wb")
    34 while True:
    35 chunk = waveFile.file.read(1024 * 1024 * 10)
    fout undefined, file undefined
    NameError: name 'file' is not defined
    args = ("name 'file' is not defined",)
    with_traceback = <built-in method with_traceback of NameError object>

    キャンセル

  • 2017/04/29 20:12

    はっきりしたことは分かりませんが、cgiserver.pyはHTML内にかかれているformのfileに実際にアップロードしようとするWAVファイルを「ユーザーがブラウザー上でfileボタンを押してアップロードファイルを指定する」のと同じようにアップロードしてくれる機能を持っているのでしょうか?

    自分はcgiserver.pyについて知らないのです><

    キャンセル

  • 2017/04/29 20:18 編集

    む、自分がサンプルページのコードを読みそこなったのかもです。
    if waveFile.file:
    fout = file("tmp/tmp0000.wav", "wb")
    とかかれているのでif文のところが必要なのかも。
    ---
    すみません、上の推測はあってるようには思えませんでした。
    fout = file("tmp/tmp0000.wav","wb")
    が問題なのですが、これは参考にしたサイトのコードそのものです。しかし自分にはこの'file'の意味がわかりませんでした。fileではなくopenに変えればわかるのですが。openに変えてみたら動く気がします。
    fout = open("tmp/tmp0000.wav", "wb")

    キャンセル

  • 2017/05/01 21:45 編集

    大変ありがとうございます。ご紹介いただいたサンプルのコードを私も見ていたのですが、意味がさっぱりわかりませんでした。しかし、今回のいただいた解説でよくわかりました。また、
    アップロード機能について調べてみます。fout = open("tmp/tmp0000.wav", "wb")は、試してみたのですがダメでした。

    キャンセル

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

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