Golang Gin作为一个优秀的框架,不仅为我们提供了托管文件的能力,还为我们提供了从io.Reader,这篇文章除了介绍文件托管的使用和原理外,我们还会利用其托管io.Reader的能力,反向代理www.baidu.com网站,也就是说,我们在浏览器里访问http://localhost:8080/就可以看到百度的网站的内容了,就像百度的镜像一样。

通过这篇文章你可以学到(6000多字大章):

  1. 托管一个静态文件
  2. 托管一个目录
  3. 如何实现FTP服务器效果
  4. 自定义托管内容类型
  5. 托管一个Reader
  6. 静态文件托管原理分析
  7. Gin是如何禁止目录列表的
  8. 镜像百度网站
  9. 封装一个直接拿来用的镜像服务代理
  10. 多域名API服务聚合(API 网关?),解决CROS跨域问题

托管一个静态文件

在项目的开发中,你可能需要这么一个功能:把服务器上的JS文件暴露出来以供访问,比如让网站调用里面的JS函数等。对于这种情形,我们可以使用Gin提供的StaticFile方法很方便的完成。

1
2
3
4
5
func main() {
	router := gin.Default()
	router.StaticFile("/adobegc.log", "/tmp/adobegc.log")
	router.Run(":8080")
}

通过StaticFile方法,把文件/tmp/adobegc.log托管在网络上,并且设置访问路径为/adobegc.log,这样我们通过http://localhost:8080/adobegc.log就可以访问这个文件,看到它的内容了。

通过这种方式可以托管任何类型的文件,并且我们不用指定Content-Type,因为会自动识别。

现在我们又有一个需求,想托管很多静态文件,如果使用StaticFile方法一个个的设置会很繁琐,有没有更简单的方法呢?接着往下看。

托管一个目录

一般情况下,我们会把我们的静态文件放在一个目录中,比如我们使用Gin做网站开发的时候,可以把CSS、JS和Image这些静态资源文件都放在一个目录中,然后使用Static方法把整个目录托管,这样就可以自由访问这个目录中的所有文件了。

1
router.Static("/static", "/tmp")

只需要新增这样一行代码就可以实现,非常简单。Static方法的第一个参数是设置的相对路径,第二个参数是本机目录的绝对路径。

现在我们就可以通过http://localhost:8080/static/adobegc.log访问上一节里演示的那个adobegc.log的内容了,和直接访问http://localhost:8080/adobegc.log效果是一样的。

实现一个FTP服务器

上一节的例子,如果你在浏览器里访问http://localhost:8080/static/,你会得到404的错误,这是因为Gin做了安全措施,防止第三方恶意罗列获取你服务器上的所有文件。

但是你的需求正好是要搭建一个类似FTP的服务器,就是想把服务器上的文件件共享给其他人使用,比如下载电影等。这时候你就需要一个可以列出目录的功能了,也就是我们访问http://localhost:8080/static/可以看到/tmp/目录下的所有文件(包括文件夹),点击文件夹还可以展开看到里面的文件和文件夹,选择合适的文件进行下载,这样就是一个完整的FTP服务器了。

1
router.StaticFS("/static1", gin.Dir("/tmp", true))

这里我们用到了StaticFS方法,也是一行代码就可以搞定,为了和上个例子区分,我这里采用/static1作为相对路径。

这里的关键点在于gin.Dir函数的第二个参数,true代表可以列目录的意思。

1
2
3
// if listDirectory == true, then it works the same as http.Dir() otherwise it returns
// a filesystem that prevents http.FileServer() to list the directory files.
func Dir(root string, listDirectory bool) http.FileSystem 

现在我们启动访问http://localhost:8080/static1/就可以看到文件和文件夹列表了,和FTP服务器是类似的。

自定义托管内容类型

以上的示例都是托管一个静态文件或者目录,我们并没有太多的自定义能力,比如设置内容类型,托管一个文件的部分内容等等。

对于这类需求,Gin为我们提供了Data方法来实现,以第一节中的adobegc.log为例。

1
2
3
4
5
6
7
8
router.GET("/adobegc.log", func(c *gin.Context) {
	data, err := ioutil.ReadFile("/tmp/adobegc.log")
	if err != nil {
		c.AbortWithError(500, err)
	} else {
		c.Data(200, "text/plain; charset=utf-8", data)
	}
})

这个例子实现的效果和上面的是一样的,不一样的是我们通过c.Data这个方法来实现,这个方法有三个参数:

1
func (c *Context) Data(code int, contentType string, data []byte) 

这就为为我们自定义提供了便利,比如可以指定contentType和内容data,这种能力很有用,比如我们可以把我们储存在数据库中的图片二进制数据,作为一张图片显示在网站上。

功能更强大的Reader托管。

除了可以从一个字节数组[]byte中读取数据显示外,Gin还为我们提供了从一个io.Reader中获取数据,并且提供了更强大的自定义能力,它就是DataFromReader方法。

1
func (c *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string)

从上面的方法签名我们可以看到我们可以自定义的内容:

  1. 要显示的内容长度
  2. 内容的类型
  3. 一个内容源Reader
  4. 响应的头信息extraHeaders

尤其是自定义的头信息,可以让我们做很多事情,比如缓存等。这个方法的使用比较简单,和上面的Data方法差不多,这里不再举例,后面我们会通过镜像百度网站这个示例来演示它的使用

基于源代码分析原理

会使用和知道原理是两码事,面试的时候,面试官也喜欢问具体的源代码实现,这样才能更看出一个人的能力。接下来我们就基于源代码来分析静态文件是如何托管成文件服务的、Gin如果实现安全的防目录列表的;然后会通过Gin的这些能力,镜像一个百度的网站(反向代理),让你访问localhost:8080就可以访问百度网站;最后会提供一个封装好的类库(直接拿来用),可以非常方便的通过Gin反向代理任意服务,通过它你可以实现聚和多个域名上的API服务,可以解决浏览器跨域的问题。

首先我们先从最简单的StaticFile方法开始。

剩余更多6000字大章,请访问 https://mp.weixin.qq.com/s/nE2khjWF0A1AOexf3gmHQw

精彩文章推荐

Golang Gin 实战(十三)| 中间件详解看这一篇就够了

Golang Gin 实战(十二)| ProtoBuf 使用和源码分析原理实现

Golang Gin 实战(十一)| HTML模板渲染

Golang Gin 实战(十)| XML渲染

Golang Gin 实战(九)| JSONP跨域和劫持

我有几个的Go语言交流微信群,可以扫码关注公众号flysnow_org或者网站 https://www.flysnow.org/,加我好友,我拉你进来。

扫码关注