元々のブログ投稿: https://lazy.bearblog.dev/go-mime-wtf/
ある週末、私は ToDo リストや買い物リストを自動化するために自分用の Telegram ボットを作成することにしました。
アイデアはシンプルです。購入するアイテムのリストをボットに送信すると、片手に携帯電話を持ち、もう一方の手に買い物かごを持った状態で、チェックボックスをオンにできるチェックボックスを含む Web ページが作成されます。
ボットはすぐに作成できましたが、その後、OpenAI の Whisper モデルを試してみたくなりました。そこで、アプリケーションに音声認識を追加することにしました。
これで、プログラムは次のように動作します:
[ ] potatoes - 1 kg [ ] dill - 1 bunch [ ] beer - 2 bottles
さて、記事について。 go-openai では、openai.AudioRequest 構造体を使用してオーディオを送信できます。これにより、Reader フィールドにオーディオ データ ストリームを指定するか、FilePath フィールドにファイル パスを指定できます。これらのフィールドは相互に排他的だと思っていましたが、オーディオ ストリームを使用する場合でも FilePath を指定する必要があることがわかりました。 API はおそらくこれを使用してストリーム タイプを決定します。
Telegram 側では、MimeType フィールドを持つ tgbotapi.Voice 構造体を受け取ります。音声メッセージの場合は audio/ogg ですが、将来簡単に変更される可能性があります。
したがって、オーディオ ストリームの MIME タイプがわかったので、そこからファイル拡張子を判断し、OpenAI API が不明なファイル タイプについてエラーを出さないようにファイル名を作成する必要があります。
コードは簡単そうに見えます:
extensions, err := mime.ExtensionsByType(mimeType) if err != nil { return err } if len(extensions) == 0 { return fmt.Errorf("unsupported mime type: %s", mimeType) } ext := extensions[0] log.Printf("Assumed extension: %s", ext) fakeFileName := fmt.Sprintf("voice_message.%s", ext)
ローカルでテスト - 動作します:
2024/07/28 17:49:06 Assumed extension: oga
クラウドへのデプロイ - 機能しません:
2024/07/28 17:55:32 unsupported mime type: audio/ogg
ローカルとビルドに使用された CI/CD で Go のバージョンを確認します - それらは同じです。
MIME パッケージの動作は決定的ではなく、実行時にオペレーティング システムに /etc/mime.types ファイルが存在するかどうかとその内容に依存することが判明しました。
ここで、MIME パッケージは MIME タイプとファイル拡張子のマッピングのランタイム テーブルを読み取ります。
私は非常に真剣です。バイナリをコンパイルし、すべてのテストを実行すると、すべてがうまくいっているように見えますが、このバイナリは、同じ MIME タイプに対して想定されるファイル拡張子を、異なる環境では異なる方法で決定します。
オーディオ ファイルの既知の MIME タイプとその拡張子を取得し、mime.AddExtensionType を使用して init 関数に手動で追加しましょう。
var mimetypes = [][]string{ {"audio/amr", "amr"}, {"audio/amr-wb", "awb"}, {"audio/annodex", "axa"}, {"audio/basic", "au", "snd"}, {"audio/csound", "csd", "orc", "sco"}, {"audio/flac", "flac"}, {"audio/midi", "mid", "midi", "kar"}, {"audio/mpeg", "mpga", "mpega", "mp2", "mp3", "m4a"}, {"audio/mpegurl", "m3u"}, {"audio/ogg", "oga", "ogg", "opus", "spx"}, {"audio/prs.sid", "sid"}, {"audio/x-aiff", "aif", "aiff", "aifc"}, {"audio/x-gsm", "gsm"}, {"audio/x-mpegurl", "m3u"}, {"audio/x-ms-wma", "wma"}, {"audio/x-ms-wax", "wax"}, {"audio/x-pn-realaudio", "ra", "rm", "ram"}, {"audio/x-realaudio", "ra"}, {"audio/x-scpls", "pls"}, {"audio/x-sd2", "sd2"}, {"audio/x-wav", "wav"}, } func init() { log.Println("init mimetypes") for _, v := range mimetypes { typ := v[0] extensions := v[1:] for _, ext := range extensions { err := mime.AddExtensionType("."+ext, typ) if err != nil { log.Fatalf("mime: %s", err.Error()) } } } }
これにより、アプリケーションが動作するドメイン (私の場合はオーディオ ファイル) に関してアプリケーションの動作が決定的になります。
この経験は、Go の物事が必ずしも決定的ではないことを示しました。何かが奇妙に思われる場合は、ためらわずにライブラリのソース コードを調べて、それがどのように実装されているかを確認してください。これにより、長期的には多くの時間とトラブルを節約できます。
以上がGo の「mime」パッケージの不確定性: 信頼するが検証するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。