>  기사  >  백엔드 개발  >  Go에서 'mime' 패키지의 불확정성: 신뢰하되 검증하세요

Go에서 'mime' 패키지의 불확정성: 신뢰하되 검증하세요

WBOY
WBOY원래의
2024-07-29 07:26:131138검색

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

원래 게시된 내용은 내 블로그입니다: https://lazy.bearblog.dev/go-mime-wtf/

배경

어느 주말, 저는 할 일이나 쇼핑 목록을 자동화하기 위해 텔레그램 봇을 직접 작성하기로 결정했습니다.

아이디어는 간단합니다. 구매할 품목 목록을 봇에 보내면 한 손에는 휴대폰을 들고 다른 한 손에는 장바구니를 들고 체크박스를 선택할 수 있는 웹페이지가 생성됩니다.

봇은 꽤 빨리 작성되었지만 OpenAI의 Whisper 모델을 실험해보고 싶었습니다. 그래서 저는 제 애플리케이션에 음성 인식을 추가하기로 결정했습니다.

이제 프로그램은 다음과 같이 작동합니다.

  • 봇에게 다음과 같은 음성 메시지를 보냅니다. "감자 1kg, 딜 한 다발, 양배추 한 개, 맥주 두 잔 사세요. 양배추는 필요 없어요. 우리가 갖고 있어요."
  • 봇은 음성을 인식하고 체크박스가 있는 페이지를 만듭니다.
[ ] 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

Go 버전을 로컬에서 확인하는 것과 빌드에 사용되는 CI/CD에서 확인하는 것은 동일합니다.

추격전을 시작하자

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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.