问题:
在学习golang的fyne时,我发现网上没有合适的教程,要么直接是复制官网的,要么直接不知道在写什么。
在官网学习到canvas的Image时,我只是想了解每一种方式的不同,但是前面几种在本地资源中加载图像都没有问题,但是在使用从url加载图像时出现了问题,找不到uri类型的定义方法。
我找遍了网上的博客,我最后评论一句:
“相似的人太多,以至于根本没有一个真正的人”
解决方法
在之前的URI在fyne根目录下,但是在v2版本中被移植到了其它包
"fyne.io/fyne/v2/storage" //在repository中实现URi类型
目录结构如下
其中URI.go这些文件都是在定义uri类型的接口或者方法,真正的定义位置在下面这里
URI结构体的定义
这就是真正被定义的位置,URI的具体类型和net/url是相似的
package repository
import (
"bufio"
"mime"
"path/filepath"
"strings"
"unicode/utf8"
"fyne.io/fyne/v2"
)
// Declare conformance with fyne.URI interface.
var _ fyne.URI = &uri{}
type uri struct {
scheme string
authority string
path string
query string
fragment string
}
URI的基础方法
func (u *uri) Extension() string {
return filepath.Ext(u.path)
}
func (u *uri) Name() string {
return filepath.Base(u.path)
}
func (u *uri) MimeType() string {
mimeTypeFull := mime.TypeByExtension(u.Extension())
if mimeTypeFull == "" {
mimeTypeFull = "text/plain"
repo, err := ForURI(u)
if err != nil {
return "application/octet-stream"
}
readCloser, err := repo.Reader(u)
if err == nil {
defer readCloser.Close()
scanner := bufio.NewScanner(readCloser)
if scanner.Scan() && !utf8.Valid(scanner.Bytes()) {
mimeTypeFull = "application/octet-stream"
}
}
}
return strings.Split(mimeTypeFull, ";")[0]
}
func (u *uri) Scheme() string {
return u.scheme
}
func (u *uri) String() string {
// NOTE: this string reconstruction is mandated by IETF RFC3986,
// section 5.3, pp. 35.
s := u.scheme + "://" + u.authority + u.path
if len(u.query) > 0 {
s += "?" + u.query
}
if len(u.fragment) > 0 {
s += "#" + u.fragment
}
return s
}
func (u *uri) Authority() string {
return u.authority
}
func (u *uri) Path() string {
return u.path
}
func (u *uri) Query() string {
return u.query
}
func (u *uri) Fragment() string {
return u.fragment
}
如何使用函数创建一个fyne.URI类型
package repository
import (
"errors"
"path/filepath"
"runtime"
"strings"
uriParser "github.com/fredbi/uri"
"fyne.io/fyne/v2"
)
// NewFileURI implements the back-end logic to storage.NewFileURI, which you
// should use instead. This is only here because other functions in repository
// need to call it, and it prevents a circular import.
//
// Since: 2.0
func NewFileURI(path string) fyne.URI {
// URIs are supposed to use forward slashes. On Windows, it
// should be OK to use the platform native filepath with UNIX
// or NT style paths, with / or \, but when we reconstruct
// the URI, we want to have / only.
if runtime.GOOS == "windows" {
// seems that sometimes we end up with
// double-backslashes
path = filepath.ToSlash(path)
}
return &uri{
scheme: "file",
path: path,
}
}
// ParseURI implements the back-end logic for storage.ParseURI, which you
// should use instead. This is only here because other functions in repository
// need to call it, and it prevents a circular import.
//
// Since: 2.0
func ParseURI(s string) (fyne.URI, error) {
// Extract the scheme.
colonIndex := strings.IndexByte(s, ':')
if colonIndex <= 0 {
return nil, errors.New("invalid URI, scheme must be present")
}
scheme := strings.ToLower(s[:colonIndex])
if scheme == "file" {
// Does this really deserve to be special? In principle, the
// purpose of this check is to pass it to NewFileURI, which
// allows platform path seps in the URI (against the RFC, but
// easier for people building URIs naively on Windows). Maybe
// we should punt this to whoever generated the URI in the
// first place?
if len(s) <= 7 {
return nil, errors.New("not a valid URI")
}
path := s[5:] // everything after file:
if len(path) > 2 && path[:2] == "//" {
path = path[2:]
}
// Windows files can break authority checks, so just return the parsed file URI
return NewFileURI(path), nil
}
repo, err := ForScheme(scheme)
if err == nil {
// If the repository registered for this scheme implements a parser
if c, ok := repo.(CustomURIRepository); ok {
return c.ParseURI(s)
}
}
// There was no repository registered, or it did not provide a parser
l, err := uriParser.Parse(s)
if err != nil {
return nil, err
}
authority := ""
if userInfo := l.Authority().UserInfo(); len(userInfo) > 0 {
authority += userInfo + "@"
}
authority += l.Authority().Host()
if port := l.Authority().Port(); len(port) > 0 {
authority += ":" + port
}
return &uri{
scheme: scheme,
authority: authority,
path: l.Authority().Path(),
query: l.Query().Encode(),
fragment: l.Fragment(),
}, nil
}
NewFileURI 使用path字符串创建(本地系统资源使用)
// NewFileURI implements the back-end logic to storage.NewFileURI, which you
// should use instead. This is only here because other functions in repository
// need to call it, and it prevents a circular import.
使用注意点:
URI 应该使用正斜杠。在Windows上,它
应该可以与 UNIX 一起使用平台本机文件路径
或 NT 样式路径,带有 或
原理:golang实现的是Unix
示例
package main
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
)
func main() {
myApp := app.New()
w := myApp.NewWindow("Image")
image := canvas.NewImageFromFile("D:\\Goland\\GinWeb\\DesktopAPP\\day03\\dome06\\纳西妲1.png") //从本地资源加载
w.SetContent(image)
w.Resize(fyne.NewSize(500, 500))
w.ShowAndRun()
}
目录:
运行结果:
ParseURI 通过解析url字符串创建URI类型(请求主要用于web)
注意:
url字符串不能有任何错误,否则就会出现请求错误,并且是这个方法需要进行错误处理,该方法相当于浏览器,不会下载图片,只会展示,不会占用大量的内存
示例
package main
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/storage"
"log"
)
func main() {
myApp := app.New()
w := myApp.NewWindow("Image")
urls, err := storage.ParseURI("https://img.vm.laomishuo.com/image/2022/09/202209071038022.jpg")//解析url字符串
if err != nil {
log.Fatal(err)
return
}
image := canvas.NewImageFromURI(urls)
w.SetContent(image)
w.Resize(fyne.NewSize(500, 500))
w.ShowAndRun()
}
运行结果:
ParseURI 用于本地文件
ParseURI 也可以用于本地文件,但是不建议,因为这样效率还不如直接使用第一种。
看一下实现就知道了
if scheme == "file" {
// Does this really deserve to be special? In principle, the
// purpose of this check is to pass it to NewFileURI, which
// allows platform path seps in the URI (against the RFC, but
// easier for people building URIs naively on Windows). Maybe
// we should punt this to whoever generated the URI in the
// first place?
if len(s) <= 7 {
return nil, errors.New("not a valid URI")
}
path := s[5:] // everything after file:
if len(path) > 2 && path[:2] == "//" {
path = path[2:]
}
// Windows files can break authority checks, so just return the parsed file URI
return NewFileURI(path), nil
}
如果你的协议头是“file”,那么最后调用的方法其实还是NewFileURI方法,这样效率低,而且浪费时间
剩下的方法
剩下其实没必要了,这两种就已经足够了,因为剩下的方法底层实现和这两种挂钩了