qr-scanner/utils/qrcode.go

116 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}