Golang从文件服务器获取图片显示到客户端
一、需求
a(客户端)---------------》b(服务端)--------------》c(文件服务器)
在客户端需要显示图片列表,但是不想c(文件服务器)的地址被暴露出来,所以现在是a(客户端)发送url到b(服务器),b(服务器)去请求c(文件服务器)的图片返回数据,b(服务器)返回图片到a(客户端)显示
注:b和c部署在不同的服务器
二、方法
1.如果c(文件服务器)是和b(服务端)部署在同一服务器,可以通过os.open(filename string)(file *file,err error)直接将图片输出;
2.如果c(文件服务器)不是和b(服务端)部署在同一服务器,可以通过获取图片的数据经过base64后转化将数据输出到客户端;
3.如果c(文件服务器)不是和b(服务端)部署在同一服务器,通过b(服务端)获取c(文件服务器)的图片数据,直接将图片返回给客户端(下面实现)
三、实现方法3
1.如何将图片可以直接输出到客户端,可根据os.open(filename string)(file *file,err error)方法来实现返回http.file接口类型即可;
2.查看golang标准库可知道http.file类型接口的实现(net/http)
type file interface { io.closer io.reader readdir(count int) ([]os.fileinfo, error) seek(offset int64, whence int) (int64, error) stat() (os.fileinfo, error) }
3.通过看http.file接口的方法返回值还需要去实现os.fileinfo类型的接口(查看文档os)
type fileinfo interface { name() string // 文件的名字(不含扩展名) size() int64 // 普通文件返回值表示其大小;其他文件的返回值含义各系统不同 mode() filemode // 文件的模式位 modtime() time.time // 文件的修改时间 isdir() bool // 等价于mode().isdir() sys() interface{} // 底层数据来源(可以返回nil) }
4.只要实现了http.file和os.fileinfo两个接口就可以返回能够输出到客户端的类型,实现以下方法
func close() (err error) func read(p []byte) (n int, err error) func readdir(count int) ([]os.fileinfo, error) func seek(offset int64, whence int) (int64, error) func stat() (os.fileinfo, error) func name() string func size() int64 func mode() os.filemode func modtime() time.time func isdir() bool func sys() interface{}
四、代码实现
package main import ( "bytes" "fmt" "io/ioutil" "net/http" "os" "path/filepath" "time" ) //实现file和fileinfo接口的类 type readimg struct { buf *bytes.reader fileurl string filedata []byte } //获取c的图片数据 func readimgdata(url string) []byte { resp, err := http.get(url) if err != nil { panic(err) } defer resp.body.close() pix, err := ioutil.readall(resp.body) if err != nil { panic(err) } return pix } //实现file和fileinfo接口 func (r *readimg) close() (err error) { return nil } func (r *readimg) read(p []byte) (n int, err error) { return r.buf.read(p) } func (r *readimg) readdir(count int) ([]os.fileinfo, error) { var i os.fileinfo = &readimg{buf: bytes.newreader(r.filedata), fileurl: r.fileurl, filedata: r.filedata} return []os.fileinfo{i}, nil } func (r *readimg) seek(offset int64, whence int) (int64, error) { return r.buf.seek(offset, whence) } func (r *readimg) stat() (os.fileinfo, error) { var i os.fileinfo = &readimg{buf: bytes.newreader(r.filedata), fileurl: r.fileurl, filedata: r.filedata} return i, nil } func (r *readimg) name() string { return filepath.base(r.fileurl)[:len(filepath.base(r.fileurl))-4] } func (r *readimg) size() int64 { return (int64)(len(r.filedata)) } func (r *readimg) mode() os.filemode { return os.modesetuid } func (r *readimg) modtime() time.time { return time.now() } func (r *readimg) isdir() bool { return false } func (r *readimg) sys() interface{} { return nil } //处理请求 type httpdealimg struct{} func (self httpdealimg) open(name string) (http.file, error) { img_name := name[1:] fmt.println(img_name) img_url := "http://localhost:8001/images/test" + name //c(文件服务器地址) img_data := readimgdata(img_url) //向服务器气球图片数据 if len(img_data) == 0 { fmt.println("file access forbidden:", name) return nil, os.errnotexist } fmt.println("get img file:", img_url) var f http.file = &readimg{buf: bytes.newreader(img_data), fileurl: img_name, filedata: img_data} //标红的可以查看标准库bytes的reader类型,newreader(p []byte)可返回*reader,然后调用和http.file相同的seek()和read()方法 return f, nil } func inithttpimgfileserv() { http.handle("/img/", http.stripprefix("/img/", http.fileserver(httpdealimg{}))) } func main() { inithttpimgfileserv() http.listenandserve(":8000", nil) }
六、测试截图
请求地址http://localhost:8000/img/qq.png
后台打印的信息是获取的是http://localhost:8001/images/test/qq.png,也就是c(文件服务器里图片的真实地址)
七、结束
当文件保存在其他的服务器上,需要在客户端显示图片,但是不想被知道真实的路径的时候就可以通过http.file和os.fileinfo去封装一下就可以实现,可能上面介绍的不清楚,有问题的可以留言一起沟通学习一下,谢谢!
如果有更好的实现方法,希望大家可以分享出来一起学习,谢谢、