最初發佈在我的部落格:https://lazy.bearblog.dev/go-mime-wtf/
一個週末,我決定為自己編寫一個 Telegram 機器人來自動執行待辦事項或購物清單。
這個想法很簡單。我向機器人發送了要購買的商品列表,它會創建一個帶有復選框的網頁,我可以在一手拿著手機、另一隻手拿著購物籃的情況下勾選這些複選框。
機器人寫得很快,但後來我想嘗試 OpenAI 的 Whisper 模型。因此,我決定在我的應用程式中添加語音識別。
現在,程式的工作原理如下:
[ ] potatoes - 1 kg [ ] dill - 1 bunch [ ] beer - 2 bottles
現在,關於這篇文章。在 go-openai 中,您可以使用 openai.AudioRequest 結構發送音頻,該結構允許您在 Reader 欄位中指定音訊資料流或在 FilePath 欄位中指定檔案路徑。好吧,我認為這些欄位是互斥的,但事實證明,即使使用音訊串流也需要指定 FilePath。 API 可能使用它來決定流類型。
在 Telegram 端,我們收到一個 tgbotapi.Voice 結構,其中有一個 MimeType 欄位。對於語音訊息,它是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中文網其他相關文章!