package utils import ( "context" "errors" "image" _ "image/jpeg" _ "image/png" "os" "time" "github.com/makiuchi-d/gozxing" multiqrcode "github.com/makiuchi-d/gozxing/multi/qrcode" "github.com/makiuchi-d/gozxing/qrcode" _ "golang.org/x/image/bmp" ) var ( ErrQRCodeNotFound = errors.New("未检测到二维码") ErrQRCodeDecodeFail = errors.New("二维码解码失败") ErrImageDecode = errors.New("图片无法解析") ) func DecodeFile(ctx context.Context, filePath string, timeout time.Duration) ([]string, error) { /* 解码封装: - 统一在此处施加“单张超时”,避免上层 caller 需要重复处理 - 使用 goroutine 执行实际解码,让 ctx 超时/取消可以及时返回 - 返回值为 []string:支持“单图多码” */ if timeout > 0 { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, timeout) defer cancel() } type result struct { contents []string err error } ch := make(chan result, 1) go func() { contents, err := decodeFile(filePath) ch <- result{contents: contents, err: err} }() select { case <-ctx.Done(): return nil, ctx.Err() case r := <-ch: return r.contents, r.err } } func decodeFile(filePath string) ([]string, error) { /* 识别策略: 1) 先尝试多码识别(QRCodeMultiReader) 2) 如果多码失败,再回退到单码识别(QRCodeReader) 失败时返回稳定的业务错误(ErrImageDecode/ErrQRCodeNotFound/ErrQRCodeDecodeFail) */ f, err := os.Open(filePath) if err != nil { return nil, err } defer f.Close() img, _, err := image.Decode(f) if err != nil { return nil, ErrImageDecode } bmp, err := gozxing.NewBinaryBitmapFromImage(img) if err != nil { return nil, ErrImageDecode } mr := multiqrcode.NewQRCodeMultiReader() rs, err := mr.DecodeMultiple(bmp, nil) if err == nil && len(rs) > 0 { return uniqueTexts(rs), nil } r := qrcode.NewQRCodeReader() single, err2 := r.Decode(bmp, nil) if err2 == nil && single != nil { return []string{single.GetText()}, nil } if err2 != nil { return nil, ErrQRCodeDecodeFail } return nil, ErrQRCodeNotFound } func uniqueTexts(rs []*gozxing.Result) []string { seen := map[string]struct{}{} out := make([]string, 0, len(rs)) for _, r := range rs { if r == nil { continue } t := r.GetText() if t == "" { continue } if _, ok := seen[t]; ok { continue } seen[t] = struct{}{} out = append(out, t) } return out }