一、需求
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去封裝一下就可以實現,可能上面介紹的不清楚,有問題的可以留言一起溝通學習一下,謝謝!
如果有更好的實現方法,希望大家可以分享出來一起學習,謝謝、