Golang Gin實踐 連載十五 產生二維碼、合并海報

來源:互聯網
上載者:User

Golang Gin實踐 連載十五 產生二維碼、合并海報

原文地址:Golang Gin實踐 連載十五 產生二維碼、合并海報
項目地址:https://github.com/EDDYCJY/go...

如果對你有所協助,歡迎點個 Star

前言

在本章節,將實現如下功能細項:

1、產生二維碼

2、合并海報(背景圖 + 二維碼)

實現

首先,你需要在 App 配置項中增加二維碼及其海報的儲存路徑,我們約定配置項名稱為 QrCodeSavePath,值為 qrcode/

經過多節連載的你應該能夠完成,若有不懂可參照 go-gin-example

產生二維碼

安裝

$ go get -u github.com/boombuler/barcode

工具包

考慮產生二維碼這一動作貼合工具包的定義,且有公用的可能性,建立 pkg/qrcode/qrcode.go 檔案,寫入內容:

package qrcodeimport (    "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 4:2:0 基準格式寫入檔案

另外在 jpeg.Encode(f, code, nil) 中,第三個參數可設定其映像品質,預設值為 75

// 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}

路由方法

1、第一步

在 routers/api/v1/article.go 新增 GenerateArticlePoster 方法用於介面開發

2、第二步

在 routers/router.go 的 apiv1 中新增 apiv1.POST("/articles/poster/generate", v1.GenerateArticlePoster) 路由

3、第三步

修改 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)

通過檢查兩個點確定功能是否正常,如下:

1、訪問結果是否 200

2、本地目錄是否成功產生二維碼圖片

合并海報

在這一節,將實現二維碼圖片與背景圖合并成新的一張圖,可用於常見的宣傳海報等業務情境

背景圖

將背景圖另存新檔 runtime/qrcode/bg.jpg(實際應用,可存在 OSS 或其他地方)

service 方法

開啟 service/article_service 目錄,建立 article_poster.go 檔案,寫入內容:

package article_serviceimport (    "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() 方法,做了如下事情:

  • 擷取二維碼儲存路徑
  • 產生二維碼映像
  • 檢查合并後映像(指的是存放合并後的海報)是否存在
  • 若不存在,則產生待合并的映像 mergedF
  • 開啟事先存放的背景圖 bgF
  • 開啟產生的二維碼映像 qrF
  • 解碼 bgF 和 qrF 返回 image.Image
  • 建立一個新的 RGBA 映像
  • 在 RGBA 映像上繪製 背景圖(bgF)
  • 在已繪製背景圖的 RGBA 映像上,在指定 Point 上繪製二維碼映像(qrF)
  • 將繪製好的 RGBA 映像以 JPEG 4:2:0 基準格式寫入合并後的影像檔(mergedF)

錯誤碼

新增 錯誤碼,錯誤提示

路由方法

開啟 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,    })}

這塊涉及到大量知識,強烈建議閱讀下,如下:

  • image.Rect
  • image.Pt
  • image.NewRGBA
  • jpeg.Encode
  • jpeg.Decode
  • draw.Op
  • draw.Draw
  • go-imagedraw-package

其所涉及、關聯的庫都建議研究一下

StaticFS

在 routers/router.go 檔案,增加如下代碼:

r.StaticFS("/qrcode", http.Dir(qrcode.GetQrCodeFullPath()))

驗證

訪問完整的 URL 路徑,返回合成後的海報並掃除二維碼成功則正確

總結

在本章節實現了兩個很常見的業務功能,分別是產生二維碼和合并海報。希望你能夠仔細閱讀我給出的連結,這塊的知識量不少,想要用好影像處理的功能,必須理解對應的思路,舉一反三

最後希望對你有所協助

參考

本系列範例程式碼

  • go-gin-example
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.