最初发布在我的博客: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中文网其他相关文章!