|
|
@@ -0,0 +1,286 @@
|
|
|
+package tool_controller
|
|
|
+
|
|
|
+import (
|
|
|
+ "bytes"
|
|
|
+ "context"
|
|
|
+ "fmt"
|
|
|
+ "image"
|
|
|
+ _ "image/jpeg"
|
|
|
+ _ "image/png"
|
|
|
+ "strings"
|
|
|
+ "time"
|
|
|
+
|
|
|
+ "github.com/chromedp/chromedp"
|
|
|
+
|
|
|
+ // "os"
|
|
|
+ "net/http"
|
|
|
+ "net/url"
|
|
|
+ // "strings"
|
|
|
+
|
|
|
+ "github.com/astaxie/beego"
|
|
|
+ // "github.com/astaxie/beego/context"
|
|
|
+ "fohow.com/apps"
|
|
|
+ "github.com/go-wkhtmltoimage"
|
|
|
+ "github.com/skip2/go-qrcode"
|
|
|
+ // "fohow.com/apps"
|
|
|
+ // "api.com/apps/controllers/user_controller"
|
|
|
+ // "api.com/apps/models/order_model"
|
|
|
+ // "api.com/apps/models/product_model"
|
|
|
+ // "api.com/apps/models/vas_model"
|
|
|
+ // qrcode "github.com/skip2/go-qrcode"
|
|
|
+)
|
|
|
+
|
|
|
+func (self *ToolController) GetHaibao() {
|
|
|
+ uri := self.GetString("url")
|
|
|
+ // if !isWhiteUrl(uri) {
|
|
|
+ // beego.BeeLogger.Error("BlackUrl=%s", uri)
|
|
|
+ // self.ReturnError(403, apps.BlackUrl, "", nil)
|
|
|
+ // }
|
|
|
+ h, _ := self.GetInt("h")
|
|
|
+ w, _ := self.GetInt("w")
|
|
|
+ q, _ := self.GetInt("q")
|
|
|
+ if h <= 0 {
|
|
|
+ h = 0 //default 0
|
|
|
+ }
|
|
|
+ if w <= 0 {
|
|
|
+ w = 1024 //default 1024
|
|
|
+ }
|
|
|
+ if q <= 0 {
|
|
|
+ q = 94 //0-100, default 94
|
|
|
+ }
|
|
|
+ c := wkhtmltoimage.ImageOptions{
|
|
|
+ BinaryPath: beego.AppConfig.String("WkhtmltoimgBinPath"),
|
|
|
+ Input: uri,
|
|
|
+ Height: h,
|
|
|
+ Width: w,
|
|
|
+ Quality: q,
|
|
|
+ Format: "png"}
|
|
|
+ out, _ := wkhtmltoimage.GenerateImage(&c)
|
|
|
+ self.Ctx.Output.Header("Content-Type", "image/png; charset=utf-8")
|
|
|
+ self.Ctx.Output.Body(out)
|
|
|
+}
|
|
|
+
|
|
|
+func (self *ToolController) GetHaibaoWithGoogle() {
|
|
|
+ uri := self.GetString("url")
|
|
|
+
|
|
|
+ // URL 解码
|
|
|
+ decodedUri, err := url.QueryUnescape(uri)
|
|
|
+ if err != nil {
|
|
|
+ beego.BeeLogger.Warning("GetHaibaoWithGoogle decode url failed: err=[%s] raw_url=[%s]", err, maskURLForLog(uri))
|
|
|
+ self.ReturnError(400, apps.ParamsError, "", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ beego.BeeLogger.Info("GetHaibaoWithGoogle start url=[%s]", maskURLForLog(decodedUri))
|
|
|
+ //title := self.GetString("title")
|
|
|
+ // 创建上下文
|
|
|
+ ctx, cancel := chromedp.NewContext(context.Background(), chromedp.WithErrorf(chromedpErrorLogf))
|
|
|
+ defer cancel()
|
|
|
+
|
|
|
+ // 设置超时时间
|
|
|
+ ctx, cancel = context.WithTimeout(ctx, 60*time.Second)
|
|
|
+ defer cancel()
|
|
|
+
|
|
|
+ // 运行任务(导航到网页,并捕获屏幕截图)
|
|
|
+ err = chromedp.Run(ctx,
|
|
|
+ chromedp.EmulateViewport(1400, 800), // 设置视口宽度为 1400,高度为 800
|
|
|
+ chromedp.Navigate(decodedUri), // 替换为你想截图的网页地址
|
|
|
+ chromedp.WaitReady("body", chromedp.ByQuery),
|
|
|
+ )
|
|
|
+ if err != nil {
|
|
|
+ beego.BeeLogger.Error("GetHaibaoWithGoogle screenshot failed: err=[%s] url=[%s]", err, maskURLForLog(decodedUri))
|
|
|
+ self.ReturnError(504, apps.NetworkBusy, "", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ buf, err := captureNonBlankScreenshot(ctx, maskURLForLog(decodedUri), 60)
|
|
|
+ if err != nil {
|
|
|
+ beego.BeeLogger.Error("GetHaibaoWithGoogle screenshot failed: err=[%s] url=[%s]", err, maskURLForLog(decodedUri))
|
|
|
+ self.ReturnError(504, apps.NetworkBusy, "", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ beego.BeeLogger.Info("GetHaibaoWithGoogle success url=[%s] bytes=[%d]", maskURLForLog(decodedUri), len(buf))
|
|
|
+ self.Ctx.Output.Header("Content-Type", http.DetectContentType(buf))
|
|
|
+ self.Ctx.Output.Body(buf)
|
|
|
+
|
|
|
+ //log.Println("Screenshot saved as screenshot.png")
|
|
|
+}
|
|
|
+
|
|
|
+func (self *ToolController) GetOtherHaibaoWithGoogle() {
|
|
|
+ uri := self.GetString("url")
|
|
|
+
|
|
|
+ // URL 解码
|
|
|
+ decodedUri, err := url.QueryUnescape(uri)
|
|
|
+ if err != nil {
|
|
|
+ beego.BeeLogger.Warning("GetOtherHaibaoWithGoogle decode url failed: err=[%s] raw_url=[%s]", err, maskURLForLog(uri))
|
|
|
+ self.ReturnError(400, apps.ParamsError, "", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ beego.BeeLogger.Info("GetOtherHaibaoWithGoogle start url=[%s]", maskURLForLog(decodedUri))
|
|
|
+ //title := self.GetString("title")
|
|
|
+ // 创建上下文
|
|
|
+ ctx, cancel := chromedp.NewContext(context.Background(), chromedp.WithErrorf(chromedpErrorLogf))
|
|
|
+ defer cancel()
|
|
|
+
|
|
|
+ // 设置超时时间
|
|
|
+ ctx, cancel = context.WithTimeout(ctx, 60*time.Second)
|
|
|
+ defer cancel()
|
|
|
+
|
|
|
+ // 运行任务(导航到网页,并捕获屏幕截图)
|
|
|
+ err = chromedp.Run(ctx,
|
|
|
+ chromedp.EmulateViewport(1400, 800), // 设置视口宽度为 1400,高度为 800
|
|
|
+ chromedp.Navigate(decodedUri), // 替换为你想截图的网页地址
|
|
|
+ chromedp.WaitReady("body", chromedp.ByQuery), // 等待页面的 <body> 元素加载完成
|
|
|
+ )
|
|
|
+ if err != nil {
|
|
|
+ beego.BeeLogger.Error("GetOtherHaibaoWithGoogle screenshot failed: err=[%s] url=[%s]", err, maskURLForLog(decodedUri))
|
|
|
+ self.ReturnError(504, apps.NetworkBusy, "", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ buf, err := captureNonBlankScreenshot(ctx, maskURLForLog(decodedUri), 90)
|
|
|
+ if err != nil {
|
|
|
+ beego.BeeLogger.Error("GetOtherHaibaoWithGoogle screenshot failed: err=[%s] url=[%s]", err, maskURLForLog(decodedUri))
|
|
|
+ self.ReturnError(504, apps.NetworkBusy, "", nil)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ beego.BeeLogger.Info("GetOtherHaibaoWithGoogle success url=[%s] bytes=[%d]", maskURLForLog(decodedUri), len(buf))
|
|
|
+ self.Ctx.Output.Header("Content-Type", http.DetectContentType(buf))
|
|
|
+ self.Ctx.Output.Body(buf)
|
|
|
+
|
|
|
+ //log.Println("Screenshot saved as screenshot.png")
|
|
|
+}
|
|
|
+
|
|
|
+func (self *ToolController) GetQrcode() {
|
|
|
+ uri := self.GetString("url")
|
|
|
+ // if !isWhiteUrl(uri) {
|
|
|
+ // beego.BeeLogger.Error("BlackUrl=%s", uri)
|
|
|
+ // self.ReturnError(403, apps.BlackUrl, "", nil)
|
|
|
+ // }
|
|
|
+ size, _ := self.GetInt("size")
|
|
|
+ if size <= 0 {
|
|
|
+ size = 256 //default 0
|
|
|
+ }
|
|
|
+ png, _ := createQrcode(uri, size)
|
|
|
+ self.Ctx.Output.Header("Content-Type", "image/png; charset=utf-8")
|
|
|
+ self.Ctx.Output.Body(png)
|
|
|
+}
|
|
|
+
|
|
|
+func createQrcode(uri string, size int) ([]byte, error) {
|
|
|
+ png, err := qrcode.Encode(uri, qrcode.Medium, size)
|
|
|
+ return png, err
|
|
|
+}
|
|
|
+func isWhiteUrl(uri string) bool {
|
|
|
+ if beego.AppConfig.String("RunMode") == "dev" {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ _uri, _ := url.Parse(uri)
|
|
|
+ checkUri := fmt.Sprintf("%s://%s", _uri.Scheme, _uri.Host)
|
|
|
+ var urls []string
|
|
|
+ urls = append(urls, beego.AppConfig.String("WxHost"))
|
|
|
+ urls = append(urls, beego.AppConfig.String("ApiHost"))
|
|
|
+ urls = append(urls, beego.AppConfig.String("RailsHost"))
|
|
|
+ urls = append(urls, "http://tapi.d5c360.com")
|
|
|
+ urls = append(urls, "https://m.d5ct.com")
|
|
|
+ urls = append(urls, "http://m.d5c360.com")
|
|
|
+ urls = append(urls, "http://www.d5c360.com")
|
|
|
+ urls = append(urls, "http://testadmin.zhppp.com")
|
|
|
+ urls = append(urls, "http://weixin.qq.com")
|
|
|
+ for _, v := range urls {
|
|
|
+ if v == checkUri {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+func maskURLForLog(rawURL string) string {
|
|
|
+ if rawURL == "" {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+ u, err := url.Parse(rawURL)
|
|
|
+ if err != nil {
|
|
|
+ return rawURL
|
|
|
+ }
|
|
|
+ q := u.Query()
|
|
|
+ if q.Get("token") != "" {
|
|
|
+ q.Set("token", "***")
|
|
|
+ }
|
|
|
+ u.RawQuery = q.Encode()
|
|
|
+ return u.String()
|
|
|
+}
|
|
|
+
|
|
|
+func chromedpErrorLogf(format string, args ...interface{}) {
|
|
|
+ msg := fmt.Sprintf(format, args...)
|
|
|
+ if strings.Contains(msg, "could not unmarshal event") {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ beego.BeeLogger.Error("chromedp error: %s", msg)
|
|
|
+}
|
|
|
+
|
|
|
+func captureNonBlankScreenshot(ctx context.Context, safeURL string, quality int) ([]byte, error) {
|
|
|
+ waitPlan := []time.Duration{
|
|
|
+ 3 * time.Second,
|
|
|
+ 3 * time.Second,
|
|
|
+ 5 * time.Second,
|
|
|
+ 8 * time.Second,
|
|
|
+ 10 * time.Second,
|
|
|
+ }
|
|
|
+ var last []byte
|
|
|
+ for attempt, wait := range waitPlan {
|
|
|
+ var buf []byte
|
|
|
+ if err := chromedp.Run(ctx,
|
|
|
+ chromedp.Sleep(wait),
|
|
|
+ chromedp.FullScreenshot(&buf, quality),
|
|
|
+ ); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ last = buf
|
|
|
+ if !isBlankScreenshot(buf) {
|
|
|
+ if attempt > 0 {
|
|
|
+ beego.BeeLogger.Info("haibao screenshot ready after retry url=[%s] attempt=[%d] bytes=[%d]", safeURL, attempt+1, len(buf))
|
|
|
+ }
|
|
|
+ return buf, nil
|
|
|
+ }
|
|
|
+ beego.BeeLogger.Warning("haibao screenshot blank, retry url=[%s] attempt=[%d] bytes=[%d]", safeURL, attempt+1, len(buf))
|
|
|
+ }
|
|
|
+ return nil, fmt.Errorf("screenshot stayed blank after retries, last_bytes=%d", len(last))
|
|
|
+}
|
|
|
+
|
|
|
+func isBlankScreenshot(data []byte) bool {
|
|
|
+ if len(data) == 0 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ img, _, err := image.Decode(bytes.NewReader(data))
|
|
|
+ if err != nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ bounds := img.Bounds()
|
|
|
+ width := bounds.Dx()
|
|
|
+ height := bounds.Dy()
|
|
|
+ if width <= 0 || height <= 0 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ stepX := maxInt(width/90, 1)
|
|
|
+ stepY := maxInt(height/90, 1)
|
|
|
+ total := 0
|
|
|
+ nonWhite := 0
|
|
|
+ for y := bounds.Min.Y; y < bounds.Max.Y; y += stepY {
|
|
|
+ for x := bounds.Min.X; x < bounds.Max.X; x += stepX {
|
|
|
+ r, g, b, _ := img.At(x, y).RGBA()
|
|
|
+ total++
|
|
|
+ if uint8(r>>8) < 245 || uint8(g>>8) < 245 || uint8(b>>8) < 245 {
|
|
|
+ nonWhite++
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return total == 0 || float64(nonWhite)/float64(total) < 0.001
|
|
|
+}
|
|
|
+
|
|
|
+func maxInt(a, b int) int {
|
|
|
+ if a > b {
|
|
|
+ return a
|
|
|
+ }
|
|
|
+ return b
|
|
|
+}
|