一、需求
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去封装一下就可以实现,可能上面介绍的不清楚,有问题的可以留言一起沟通学习一下,谢谢!
如果有更好的实现方法,希望大家可以分享出来一起学习,谢谢、