Maison  >  Article  >  développement back-end  >  Indétermination du package « mime » dans Go : faites confiance mais vérifiez

Indétermination du package « mime » dans Go : faites confiance mais vérifiez

WBOY
WBOYoriginal
2024-07-29 07:26:131208parcourir

Indeterminacy of the `mime` Package in Go: Trust But Verify

Publié à l'origine sur mon blog : https://lazy.bearblog.dev/go-mime-wtf/

Arrière-plan

Un week-end, j'ai décidé d'écrire un robot Telegram pour automatiser les listes de tâches ou de courses.

L'idée est simple. J'envoie au robot une liste d'articles à acheter et il crée une page Web avec des cases à cocher que je peux cocher tout en tenant mon téléphone dans une main et un panier dans l'autre.

Le bot a été écrit assez rapidement, mais j'ai ensuite voulu expérimenter le modèle Whisper d'OpenAI. J'ai donc décidé d'ajouter la reconnaissance vocale à mon application.

Maintenant, le programme fonctionne comme suit :

  • J'envoie au bot un message vocal comme celui-ci : "achète un kilo de pommes de terre, un bouquet d'aneth, une tête de chou, quelques bières, oh, pas besoin de chou, nous l'avons"
  • Le bot reconnaît le discours et crée une page avec des cases à cocher :
[ ] potatoes - 1 kg
[ ] dill - 1 bunch
[ ] beer - 2 bottles

Maintenant, à propos de l'article. Dans go-openai, vous pouvez envoyer de l'audio à l'aide de la structure openai.AudioRequest, qui vous permet de spécifier un flux de données audio dans le champ Reader ou un chemin de fichier dans le champ FilePath. Eh bien, je pensais que ces champs s'excluaient mutuellement, mais il s'avère que vous devez spécifier FilePath même lorsque vous utilisez un flux audio. L'API l'utilise probablement pour déterminer le type de flux.

Côté Telegram, on reçoit une structure tgbotapi.Voice, qui possède un champ MimeType. Pour les messages vocaux, c'est audio/ogg, mais cela pourrait facilement changer dans le futur.

Nous avons donc le type MIME du flux audio, et nous devons en déterminer l'extension de fichier, en créant un nom de fichier pour garantir que l'API OpenAI ne se plaint pas d'un type de fichier inconnu.

Quoi de plus simple ?

Le code semble trivial :

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)

Tester localement - ça marche :

2024/07/28 17:49:06 Assumed extension: oga

Déploiement sur le cloud - ça ne marche pas :

2024/07/28 17:55:32 unsupported mime type: audio/ogg

Vérification des versions Go, localement et dans le CI/CD utilisé pour la construction - ce sont les mêmes.

Allons droit au but

Il s'avère que le comportement du package MIME n'est pas déterministe et dépend au moment de l'exécution de la présence ou non d'un fichier /etc/mime.types dans le système d'exploitation et de son contenu.

C'est là que le package MIME lit la table d'exécution du type MIME vers les mappages d'extension de fichier.

Je suis très sérieux : vous compilez un binaire, exécutez tous les tests, tout semble bien, mais ce binaire déterminera les extensions de fichier supposées pour le même type MIME différemment dans différents environnements.

Comment y remédier

Prenons les types MIME connus de fichiers audio et leurs extensions, et ajoutons-les manuellement dans la fonction init en utilisant mime.AddExtensionType.

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())
            }
        }
    }
}

Cela rendra le comportement de notre application déterministe quant au domaine avec lequel elle fonctionnera (dans mon cas, les fichiers audio).

Conclusion

Cette expérience a montré que les choses en Go ne sont pas toujours déterministes. Si quelque chose vous semble étrange, n'hésitez pas à vous plonger dans le code source de la bibliothèque et à voir comment il est implémenté. Cela peut vous épargner beaucoup de temps et d'ennuis à long terme.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn