原文地址:Golang Gin實踐 連載十五 生成二維碼、合併海報
項目地址:https://github.com/EDDYCJY/go...html
若是對你有所幫助,歡迎點個 Star 👍git
在本章節,將實現以下功能細項:github
一、生成二維碼golang
二、合併海報(背景圖 + 二維碼)segmentfault
首先,你須要在 App 配置項中增長二維碼及其海報的存儲路徑,咱們約定配置項名稱爲 QrCodeSavePath
,值爲 qrcode/
api
通過多節連載的你應該可以完成,如有不懂可參照 go-gin-example緩存
$ go get -u github.com/boombuler/barcode
考慮生成二維碼這一動做貼合工具包的定義,且有公用的可能性,新建 pkg/qrcode/qrcode.go 文件,寫入內容:app
package qrcode import ( "image/jpeg" "github.com/boombuler/barcode" "github.com/boombuler/barcode/qr" "github.com/EDDYCJY/go-gin-example/pkg/file" "github.com/EDDYCJY/go-gin-example/pkg/setting" "github.com/EDDYCJY/go-gin-example/pkg/util" ) type QrCode struct { URL string Width int Height int Ext string Level qr.ErrorCorrectionLevel Mode qr.Encoding } const ( EXT_JPG = ".jpg" ) func NewQrCode(url string, width, height int, level qr.ErrorCorrectionLevel, mode qr.Encoding) *QrCode { return &QrCode{ URL: url, Width: width, Height: height, Level: level, Mode: mode, Ext: EXT_JPG, } } func GetQrCodePath() string { return setting.AppSetting.QrCodeSavePath } func GetQrCodeFullPath() string { return setting.AppSetting.RuntimeRootPath + setting.AppSetting.QrCodeSavePath } func GetQrCodeFullUrl(name string) string { return setting.AppSetting.PrefixUrl + "/" + GetQrCodePath() + name } func GetQrCodeFileName(value string) string { return util.EncodeMD5(value) } func (q *QrCode) GetQrCodeExt() string { return q.Ext } func (q *QrCode) CheckEncode(path string) bool { src := path + GetQrCodeFileName(q.URL) + q.GetQrCodeExt() if file.CheckNotExist(src) == true { return false } return true } func (q *QrCode) Encode(path string) (string, string, error) { name := GetQrCodeFileName(q.URL) + q.GetQrCodeExt() src := path + name if file.CheckNotExist(src) == true { code, err := qr.Encode(q.URL, q.Level, q.Mode) if err != nil { return "", "", err } code, err = barcode.Scale(code, q.Width, q.Height) if err != nil { return "", "", err } f, err := file.MustOpen(name, path) if err != nil { return "", "", err } defer f.Close() err = jpeg.Encode(f, code, nil) if err != nil { return "", "", err } } return name, path, nil }
這裏主要聚焦 func (q *QrCode) Encode
方法,作了以下事情:工具
另外在 jpeg.Encode(f, code, nil)
中,第三個參數可設置其圖像質量,默認值爲 75post
// DefaultQuality is the default quality encoding parameter. const DefaultQuality = 75 // Options are the encoding parameters. // Quality ranges from 1 to 100 inclusive, higher is better. type Options struct { Quality int }
一、第一步
在 routers/api/v1/article.go 新增 GenerateArticlePoster 方法用於接口開發
二、第二步
在 routers/router.go 的 apiv1 中新增 apiv1.POST("/articles/poster/generate", v1.GenerateArticlePoster)
路由
三、第三步
修改 GenerateArticlePoster 方法,編寫對應的生成邏輯,以下:
const ( QRCODE_URL = "https://github.com/EDDYCJY/blog#gin%E7%B3%BB%E5%88%97%E7%9B%AE%E5%BD%95" ) func GenerateArticlePoster(c *gin.Context) { appG := app.Gin{c} qrc := qrcode.NewQrCode(QRCODE_URL, 300, 300, qr.M, qr.Auto) path := qrcode.GetQrCodeFullPath() _, _, err := qrc.Encode(path) if err != nil { appG.Response(http.StatusOK, e.ERROR, nil) return } appG.Response(http.StatusOK, e.SUCCESS, nil) }
經過 POST 方法訪問 http://127.0.0.1:8000/api/v1/articles/poster/generate?token=$token
(注意 $token)
經過檢查兩個點肯定功能是否正常,以下:
一、訪問結果是否 200
二、本地目錄是否成功生成二維碼圖片
在這一節,將實現二維碼圖片與背景圖合併成新的一張圖,可用於常見的宣傳海報等業務場景
將背景圖另存爲 runtime/qrcode/bg.jpg(實際應用,可存在 OSS 或其餘地方)
打開 service/article_service 目錄,新建 article_poster.go 文件,寫入內容:
package article_service import ( "image" "image/draw" "image/jpeg" "os" "github.com/EDDYCJY/go-gin-example/pkg/file" "github.com/EDDYCJY/go-gin-example/pkg/qrcode" ) type ArticlePoster struct { PosterName string *Article Qr *qrcode.QrCode } func NewArticlePoster(posterName string, article *Article, qr *qrcode.QrCode) *ArticlePoster { return &ArticlePoster{ PosterName: posterName, Article: article, Qr: qr, } } func GetPosterFlag() string { return "poster" } func (a *ArticlePoster) CheckMergedImage(path string) bool { if file.CheckNotExist(path+a.PosterName) == true { return false } return true } func (a *ArticlePoster) OpenMergedImage(path string) (*os.File, error) { f, err := file.MustOpen(a.PosterName, path) if err != nil { return nil, err } return f, nil } type ArticlePosterBg struct { Name string *ArticlePoster *Rect *Pt } type Rect struct { Name string X0 int Y0 int X1 int Y1 int } type Pt struct { X int Y int } func NewArticlePosterBg(name string, ap *ArticlePoster, rect *Rect, pt *Pt) *ArticlePosterBg { return &ArticlePosterBg{ Name: name, ArticlePoster: ap, Rect: rect, Pt: pt, } } func (a *ArticlePosterBg) Generate() (string, string, error) { fullPath := qrcode.GetQrCodeFullPath() fileName, path, err := a.Qr.Encode(fullPath) if err != nil { return "", "", err } if !a.CheckMergedImage(path) { mergedF, err := a.OpenMergedImage(path) if err != nil { return "", "", err } defer mergedF.Close() bgF, err := file.MustOpen(a.Name, path) if err != nil { return "", "", err } defer bgF.Close() qrF, err := file.MustOpen(fileName, path) if err != nil { return "", "", err } defer qrF.Close() bgImage, err := jpeg.Decode(bgF) if err != nil { return "", "", err } qrImage, err := jpeg.Decode(qrF) if err != nil { return "", "", err } jpg := image.NewRGBA(image.Rect(a.Rect.X0, a.Rect.Y0, a.Rect.X1, a.Rect.Y1)) draw.Draw(jpg, jpg.Bounds(), bgImage, bgImage.Bounds().Min, draw.Over) draw.Draw(jpg, jpg.Bounds(), qrImage, qrImage.Bounds().Min.Sub(image.Pt(a.Pt.X, a.Pt.Y)), draw.Over) jpeg.Encode(mergedF, jpg, nil) } return fileName, path, nil }
這裏重點留意 func (a *ArticlePosterBg) Generate()
方法,作了以下事情:
打開 routers/api/v1/article.go 文件,修改 GenerateArticlePoster 方法,編寫最終的業務邏輯(含生成二維碼及合併海報),以下:
const ( QRCODE_URL = "https://github.com/EDDYCJY/blog#gin%E7%B3%BB%E5%88%97%E7%9B%AE%E5%BD%95" ) func GenerateArticlePoster(c *gin.Context) { appG := app.Gin{c} article := &article_service.Article{} qr := qrcode.NewQrCode(QRCODE_URL, 300, 300, qr.M, qr.Auto) // 目前寫死 gin 系列路徑,可自行增長業務邏輯 posterName := article_service.GetPosterFlag() + "-" + qrcode.GetQrCodeFileName(qr.URL) + qr.GetQrCodeExt() articlePoster := article_service.NewArticlePoster(posterName, article, qr) articlePosterBgService := article_service.NewArticlePosterBg( "bg.jpg", articlePoster, &article_service.Rect{ X0: 0, Y0: 0, X1: 550, Y1: 700, }, &article_service.Pt{ X: 125, Y: 298, }, ) _, filePath, err := articlePosterBgService.Generate() if err != nil { appG.Response(http.StatusOK, e.ERROR_GEN_ARTICLE_POSTER_FAIL, nil) return } appG.Response(http.StatusOK, e.SUCCESS, map[string]string{ "poster_url": qrcode.GetQrCodeFullUrl(posterName), "poster_save_url": filePath + posterName, }) }
這塊涉及到大量知識,強烈建議閱讀下,以下:
其所涉及、關聯的庫都建議研究一下
在 routers/router.go 文件,增長以下代碼:
r.StaticFS("/qrcode", http.Dir(qrcode.GetQrCodeFullPath()))
訪問完整的 URL 路徑,返回合成後的海報並掃除二維碼成功則正確 🤓
在本章節實現了兩個很常見的業務功能,分別是生成二維碼和合並海報。但願你可以仔細閱讀我給出的連接,這塊的知識量很多,想要用好圖像處理的功能,必須理解對應的思路,觸類旁通
最後但願對你有所幫助 👌