Golang 打开浏览器 跨端方案

如何用Golang 代码唤起系统浏览器,以及在各个操作系统如何保证都能生效.官方又是怎么做的呢?

Golang 打开浏览器 跨端方案

Open browser on multi platform by Golang

用Golang 代码打开浏览器并访问网址,你用对了么?

放“码”过来

自动判断平台,生成跨端启动浏览器命令

// browsers returns a list of commands to attempt for web visualization.
func browsers() []string {
    var cmds []string
    if userBrowser := os.Getenv("BROWSER"); userBrowser != "" {
        cmds = append(cmds, userBrowser)
    }
    switch runtime.GOOS {
    case "darwin":
        cmds = append(cmds, "/usr/bin/open")
    case "windows":
        cmds = append(cmds, "cmd /c start")
    default:
        // Commands opening browsers are prioritized over xdg-open, so browser()
        // command can be used on linux to open the .svg file generated by the -web
        // command (the .svg file includes embedded javascript so is best viewed in
        // a browser).
        cmds = append(cmds, []string{"chrome", "google-chrome", "chromium", "firefox", "sensible-browser"}...)
        if os.Getenv("DISPLAY") != "" {
            // xdg-open is only for use in a desktop environment.
            cmds = append(cmds, "xdg-open")
        }
    }
    return cmds
}

启动浏览器并访问传入的网址

// open browser with url
func OpenBrowser(targetUrl string) (err error) {
    // Construct URL.
    u, _ := url.Parse(targetUrl)

    for _, b := range browsers() {
        args := strings.Split(b, " ")
        if len(args) == 0 {
            continue
        }
        viewer := exec.Command(args[0], append(args[1:], u.String())...)
        viewer.Stderr = os.Stderr
        if err = viewer.Start(); err == nil {
            return
        }
    }
    // No visualizer succeeded, so just print URL.
    fmt.Println(u.String())
    return
}

代码示例:

package main

import (
    "fmt"
    "net/url"
    "os"
    "os/exec"
    "runtime"
    "strings"
    "time"
)

func main() {
    openBrowser("http://blog.itjsz.com")
    time.Sleep(time.Second * 10)
}

初衷

当时我为了做一个开源项目—— https://github.com/PaulXu-cn/go-mod-graph-chart, 里面有个功能就是打开浏览器并访问 golang 启的 http 网页。在 Mac 下工作良好,在 Windows 下死活不唤起浏览器。

当时我查了好多资料,大多数给到我的信息是:

  • windows 下用 start
  • darwin Macopen
  • linux 统一用 xdg-open

windowsbash 环境是有点兼容性问题的。后来我用了 golang 的 pprof 工具,发现它也需要跨端唤起 浏览器,我就去研究了下它代码。

代码地址 —— google/pprof

好,希望这简短的文章能帮到你,解决 golang 跨端 唤起浏览器问题。

PS: 刚看了下,网上那么多正确方案,我抄错了,还带到了开源项目里,订在commit里面了,死死地那种。

参考

  • https://github.com/google/pprof
  • https://github.com/pkg/browser

Golang Eval 第三方实现

Golang作为静态语言,是否有Eval函数呢,今天,go-eval他来了

Go Eval 库

Golangeval() 函数第三方实现。

go eval

背景

众所周知,Golang 是一门静态语言,笔者作为动态语言转过来的老同志(别猜了我是phper),习惯了用 eval() 就想着Go 动态执行代码呢。

如何在 golang 中使用 eval() 函数,phpjavascript 自带该功能。golang 官方是没有提供相关库的。

在丰富的Go第三方生态中,着实没找到相关的库。倒是看到有人做了一个demo 挂到博客。我就来拿来改改,做成了一个库,望广大 gopher 用的满意。

安装

$ go get github.com/PaulXu-cn/goeval

功能介绍

这个 goeval 库,传入 golang 代码字符串,然后执行 eval() 函数,就能获得该 字符串代码 输出到 stdout 上的内容。

使用例子:

package main

import (
    "fmt"
    "github.com/PaulXu-cn/goeval"
)

func main() {
    if re, err := goeval.Eval(
        "",
        "fmt.Print(\"Hello http://blog.itjsz.com\")",
        "fmt"); nil == err {
        fmt.Print(string(re))
    } else {
        fmt.Print(err.Error())
    }
}

输出:

Hello http://blog.itjsz.com

这里我们引入了 goeval 包,调用它的 Eval 函数,第一个参数是,结构体定义代码(由于代码中没有有用自定义结构体,所有这里传空), 第二个字符串是要执行的代码,第三及以后的字符串是import的包。

实现细则

  1. 按照传入的字符串构建整个运行代码的字符串
  2. 格式化代码,主要是删除未使用 包,所以 import 的包可以多,但不能少。
  3. tmp 目录下随机创建一个目录,并进入
  4. 在刚创建目录里创建 main.go 文件
  5. 写入格式化后的代码
  6. 运行 go run main.go 并收集 stdout
  7. 删除创建的文件夹以及 main.go
  8. 返回 stdout

参考

  • https://golangtc.com/t/55b4ef18b09ecc22f6000219

【已解决】Laravel Class encrypter does not exist No supported encrypter found. The cipher

Laravel 报错加密类找不到。Class encrypter does not exist. No supported encrypter found.

Laravel 5.1 加密类找不到问题

Laravel 项目运行时,报错:

No supported encrypter found. The cipher and / or key length are invalid
Class encrypter does not exist

刚开始以为是,加密拓展没装好,实际上是app密钥没生成、配置到位。

解决否

已解决

方案

点断点, 断到: \Illuminate\Encryption\EncryptionServiceProvider::register

发现读取了配置,配置里面的 key 没初始化。

$ php artisan key:generate

$ php aritsan config:cache

生成的 key 要写入到配置文件里。这配置一般是在 /app/config.php 目录里,实际请按照个人项目来。

这里报错就是因为,随便写了一个字符串,长度不符合key的40长度,导致报错密钥长度不够。

WebAssembly golang 初探

Golang 也能写前端逻辑了?快来试试吧,WebAssembly

WebAssembly go

官方wiki: https://github.com/golang/go/wiki/WebAssembly

golang 版本不能低于 1.11。当然 1.13 及以上最好。

API:https://golang.org/pkg/syscall/js/

快速开始

写个golang 脚本

package main

import "fmt"

func main() {
    fmt.Println("Hello, WebAssembly!")
}

设置GOOS=js和GOARCH=wasm环境变量以编译WebAssembly:

$ GOOS=js GOARCH=wasm go build -o main.wasm

它将生成包并生成一个名为 main.wasm。 wasm文件扩展名将使以后通过HTTP提供正确的Content-Type头更容易。

请注意,您只能编译主包。否则,您将获得无法在WebAssembly中运行的对象文件。如果您有一个可以与WebAssembly一起使用的包,请将其转换为主包并构建一个二进制文件。

执行 main.wasm 在浏览器中,我们还需要一个 JavaScript 支持文件和一个HTML页面来连接所有内容。

$ cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .

HTML 文件:

<html>
    <head>
        <meta charset="utf-8"/>
        <script src="wasm_exec.js"></script>
        <script>
            const go = new Go();
            WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
                go.run(result.instance);
            });
        </script>
    </head>
    <body></body>
</html>

如果您的浏览器还不支持WebAssembly.InstanceStereaming,可以使用 polyfill

点击查看浏览器兼容性

目前就 IE 不支持吧,其他浏览器基本都支持。

把三个文件放到一边目录里,并在该目录启动一个 http server:

# install goexec: go get -u github.com/shurcooL/goexec
$ goexec 'http.ListenAndServe(`:8080`, http.FileServer(http.Dir(`.`)))'

减少Wasm文件的大小

目前,Go生成大的Wasm文件,最小的可能大小约为2MB。如果你的Go代码导入库,这个文件的大小会急剧增加。10MB+是常见的。

目前有两种主要方法可以减小此文件大小:

手动压缩.wasm文件。

使用 gz 压缩将~2MB(最小文件大小)示例WASM文件减少到 500kB 左右。使用 Zopfli 来执行gzip压缩可能更好,因为它比gzip提供更好的结果——最好,但是运行它确实需要更长的时间。

使用 Brotli 进行压缩,文件大小明显优于Zopfli和gzip——最好,压缩时间也介于两者之间。这台 (new) Brotli compressor 很合理。

来自 @johanbrandhorst 的示例

例子 1

Size Command Compression time
16M (uncompressed size) N/A
2.4M brotli -o test.wasm.br test.wasm 53.6s
3.3M go-zopfli test.wasm 3m 2.6s
3.4M gzip --best test.wasm 2.5s
3.4M gzip test.wasm 0.8s

例子 2

Size Command Compression time
2.3M (uncompressed size) N/A
496K brotli -o main.wasm.br main.wasm 5.7s
640K go-zopfli main.wasm 16.2s
660K gzip --best main.wasm 0.2s
668K gzip main.wasm 0.2s

参考

  • https://blog.alphatr.com/go-write-webassembly.html
  • https://zhuanlan.zhihu.com/p/149266343

Golang 字符串 格式化

Golang 格式化,Sprintf Printf 格式化参数

golang format

在 Go 语言中,fmt.Sprintf(), fmt.Printf(), Log.Printf() 等函数常常会用字符串格式化参数,这一篇文章带你熟悉所有参数。

参数介绍

动 词 功 能
%v 按值的本来值输出
%+v 在 %v 基础上,对结构体字段名和值进行展开
%#v 输出 Go 语言语法格式的值
%T 输出 Go 语言语法格式的类型和值
%% 输出 % 本体
%b 整型以二进制方式显示
%o 整型以八进制方式显示
%d 整型以十进制方式显示
%x 整型以十六进制方式显示
%X 整型以十六进制、字母大写方式显示
%U Unicode 字符
%f 浮点数
%p 指针,十六进制方式显示

实战例子

// Go 在传统的`printf` 中对字符串格式化提供了优异的支持。
// 这里是一些基本的字符串格式化的人物的例子。

package main

import "fmt"
import "os"

type point struct {
    x, y int
}

func main() {

    // Go 为常规 Go 值的格式化设计提供了多种打印方式。例
    // 如,这里打印了 `point` 结构体的一个实例。
    p := point{1, 2}
    fmt.Printf("%v\n", p)
    // 输出:{1 2}

    // 如果值是一个结构体,`%+v` 的格式化输出内容将包括
    // 结构体的字段名。
    fmt.Printf("%+v\n", p)
    // 输出:{x:1 y:2}

    // `%#v` 形式则输出这个值的 Go 语法表示。例如,值的
    // 运行源代码片段。
    fmt.Printf("%#v\n", p)
    // 输出:main.point{x:1, y:2}

    // 需要打印值的类型,使用 `%T`。
    fmt.Printf("%T\n", p)
    // 输出:main.point

    // 格式化布尔值是简单的。
    fmt.Printf("%t\n", true)
    // 输出:true

    // 格式化整形数有多种方式,使用 `%d`进行标准的十进
    // 制格式化。
    fmt.Printf("%d\n", 123)
    // 输出:123

    // 这个输出二进制表示形式。
    fmt.Printf("%b\n", 14)
    // 输出:1110

    // 这个输出给定整数的对应字符。
    fmt.Printf("%c\n", 33)
    // 输出:!

    // `%x` 提供十六进制编码。
    fmt.Printf("%x\n", 456)
    // 输出:1c8

    // 对于浮点型同样有很多的格式化选项。使用 `%f` 进
    // 行最基本的十进制格式化。
    fmt.Printf("%f\n", 78.9)
    // 输出:78.900000

    // `%e` 和 `%E` 将浮点型格式化为(稍微有一点不
    // 同的)科学技科学记数法表示形式。
    fmt.Printf("%e\n", 123400000.0)
    // 输出:1.234000e+08
    fmt.Printf("%E\n", 123400000.0)
    // 输出:1.234000E+08

    // 使用 `%s` 进行基本的字符串输出。
    fmt.Printf("%s\n", "\"string\"")
    // 输出:"string"

    // 像 Go 源代码中那样带有双引号的输出,使用 `%q`。
    fmt.Printf("%q\n", "\"string\"")
    // 输出:"\"string\""

    // 和上面的整形数一样,`%x` 输出使用 base-16 编码的字
    // 符串,每个字节使用 2 个字符表示。
    fmt.Printf("%x\n", "hex this")
    // 输出:6865782074686973

    // 要输出一个指针的值,使用 `%p`。
    fmt.Printf("%p\n", &p)
    // 输出:0x42135100

    // 当输出数字的时候,你将经常想要控制输出结果的宽度和
    // 精度,可以使用在 `%` 后面使用数字来控制输出宽度。
    // 默认结果使用右对齐并且通过空格来填充空白部分。
    fmt.Printf("|%6d|%6d|\n", 12, 345)
    // 输出:|    12|   345|

    // 你也可以指定浮点型的输出宽度,同时也可以通过 宽度.
    // 精度 的语法来指定输出的精度。
    fmt.Printf("|%6.2f|%6.2f|\n", 1.2, 3.45)
    // 输出:|  1.20|  3.45|

    // 要左对齐,使用 `-` 标志。
    fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45)
    // 输出:|1.20  |3.45  |

    // 你也许也想控制字符串输出时的宽度,特别是要确保他们在
    // 类表格输出时的对齐。这是基本的右对齐宽度表示。
    fmt.Printf("|%6s|%6s|\n", "foo", "b")
    // 输出:|   foo|     b|

    // 要左对齐,和数字一样,使用 `-` 标志。
    fmt.Printf("|%-6s|%-6s|\n", "foo", "b")
    // 输出:|foo   |b     |

    // 到目前为止,我们已经看过 `Printf`了,它通过 `os.Stdout`
    // 输出格式化的字符串。`Sprintf` 则格式化并返回一个字
    // 符串而不带任何输出。
    s := fmt.Sprintf("a %s", "string")
    fmt.Println(s)
    // 输出:a string

    // 你可以使用 `Fprintf` 来格式化并输出到 `io.Writers`
    // 而不是 `os.Stdout`。
    fmt.Fprintf(os.Stderr, "an %s\n", "error")
    // 输出:an error
}
  • 结果
{1 2}
{x:1 y:2}
main.point{x:1, y:2}
main.point
true
123
1110
!
1c8
78.900000
1.234000e+08
1.234000E+08
"string"
"\"string\""
6865782074686973
0xc000100010
|    12|   345|
|  1.20|  3.45|
|1.20  |3.45  |
|   foo|     b|
|foo   |b     |
a string
an error

参考

  • https://books.studygolang.com/gobyexample/string-formatting/
  • http://c.biancheng.net/view/41.html