golang实现七牛的上存文件的原理

在golang 的世界上,golang是可以作为服务端和客户端来存在的。但这个主题主要讲的时golang作为服务端的实现。

终端->golang服务器->七牛服务器

从逻辑上面,先把文件提交到golang服务器,然后golang服务器把文件上存到七牛的服务器。但通常为了更好的实现一些资源上的安排,通常是终端先把数据上存到七牛服务器,然后七牛服务器把一些数据的回调,回调到golang的服务器,虽然golang也可以当客户端终端使用。但我手上没有可以搭建golang的网络服务器的使用。


golang 七牛 v6sdk链接
github v7 sdk 链接如果是刚刚上存的话,建议使用新的sdk,因为新的sdk问题上步考虑旧的兼容性问题,以及未来可以很好的优化。

那么说到这,实现这个功能的话,我们可以考虑分为三步去实现。
1:实现golang的服务器直接上存文件到七牛服务器。
2:实现从网页上存文件到golang服务器。
3:实现把1和2合并,上存文件到七牛服务器。


实现golang的服务器直接上存文件到七牛服务器

其实从github上面的文档中可以看到,就是简单的几行代码的使用。
简单直接粗暴的方法是贴代码:

// Quniu project main.go
package main

import (
   "fmt"
   "qiniupkg.com/api.v7/kodo"
   "golang.org/x/net/context"
)

func main() {
   fmt.Println("Hello World!")
   kodo.SetMac("从七牛获得的access key","从七牛获得的secret key")
   zone:=0
   c:=kodo.New(zone,nil)
   
   bucket:=c.Bucket("appfornew")//命名空间
   ctx := context.Background()
   localFile :="/Users/youyou/Desktop/youzhuan.apk"
   err:=bucket.PutFile(ctx,nil,"foo/bar.apk",localFile,nil)
   if err!=nil{
      fmt.Println("has err")
      return
   }else{
      fmt.Println("success")
   }
}

从代码上的实现,服务器上存到就是那么几行的代码。需要注意的参数之间的含义和意思。例如localFile,这个是本地上存的地址,由于是服务器上存的,所以这个地址应该是本地服务器的地址,而不是url地址。不过七牛也提供一个api的使用,可以获取网上的资源保存到七牛的服务器中。详情留意bucket.go func Fetch()


从网页上存文件到golang服务器中

从网页上存到服务器,那么首先用什么去搭建网页呢?
可以使用原生态的go,这个没有经过任何的封装,也可以使用封装过后的框架。框架只是为了让人更好的去服务和工作。

原生态的go服务器

import (
   "net/http"
   "fmt"
   "time"
   "io"
   "strconv"
   "crypto/md5"
   "html/template"
   "os"
   "log"
)

func main() {
http.HandleFunc("/upload", upload)
log.Fatal(http.ListenAndServe(":8080", nil))
}
// 处理/upload 逻辑
func upload(w http.ResponseWriter, r *http.Request) {
    fmt.Println("method:", r.Method) //获取请求的方法
    if r.Method == "GET" {
        crutime := time.Now().Unix()
        h := md5.New()
        io.WriteString(h, strconv.FormatInt(crutime, 10))
        token := fmt.Sprintf("%x", h.Sum(nil))

        t, _ := template.ParseFiles("views/one.tpl")
        t.Execute(w, token)
    } else {
        r.ParseMultipartForm(32 << 20)
        file, handler, err := r.FormFile("uploadfile")
        if err != nil {
            fmt.Println(err)
            return
        }
      fmt.Println(r.FormValue("token"))
      fmt.Println(handler.Filename)
        defer file.Close()
        fmt.Fprintf(w, "%v", handler.Header)
      fmt.Println("post")
      
        f, err := os.OpenFile("./"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
        if err != nil {
            fmt.Println(err)
            return
        }
        defer f.Close()
        io.Copy(f, file)
    }
}

上面首先需要知道的是get和post的判断,判断是哪种方式获取页面,当然post肯定是提交文件的方法。
html页面

<html>
<head>
    <title>上传文件</title>
</head>
<body>
<form enctype="multipart/form-data" action="upload" method="post">
  <input type="file" name="uploadfile" />
  <input type="hidden" name="token" value=""/>
  <input type="submit" value="upload" />
</form>
</body>
</html>

在html页面中需要注意的是需要在form中添加 enctype="multipart/form-data"这个属性,不然的话浏览器会拒绝提交信息。action是触发事件,当然这里触发的事件是直接post到服务器的url地址中,可以用js的事件顶替使用。

那么简单的一个提交文件到服务器就这样完成了。


使用beego框架的服务器

beego的网站是 beego.me
快速安装在终端上面输入go get github.com/astaxie/beego 当然,得让这个方法生效,你得先配置好golang 的开发环境。然后继续使用配置bee,bee是一个对应beego的一个开发小工具,能有效的提高效率上的开发问题。
首先在gopath下面使用 bee new project新建一个项目,这个时候就从这个项目上搭建我们的上存问题。
从这里面看到bee生成的目录结构,从结构上分析,这是个典型的MVC的开发模式结构。
那么我们需要做什么呢?在controllers的包下面的类中实现我们的逻辑。
修改default.go文件 ,还有实现路由控制功能,修改router.go文件
下面的是default.go文件

package controllers

import (
   "github.com/astaxie/beego"
   "fmt"

type PutController struct{
   beego.Controller
}
func(c *PutController) Get(){
   c.TplNames ="one.tpl"
}
type EmailController struct{
   beego.Controller
}
func(c *PutController) Post(){
   file,fileHead,err :=c.GetFile("uploadfile")
   fmt.Println("two")
   if err!=nil{
      fmt.Println(err)
      c.Ctx.WriteString("err")
      return
   }
   defer file.Close()
   fmt.Printf(fileHead.Filename)
   err = c.SaveToFile("uploadfile","./"+fileHead.Filename)
}

从代码上看什么亮点,但要注意的是开发文档给你的坑,因为开发文档不一定是准确的,偶尔需要看一下源码。golang的源码看得很随意,想看就可以看的节奏哦~。个人理解,上面的代码中其实可以改比较多的东西,例如没必要GetFile,因为在SaveToFile里面已经有这个分装了。这里在文档里面是坑。


router.go的代码

package routers

import (
   "project/controllers"
   "github.com/astaxie/beego"
)

func init() {
   beego.Router("/upload",&controllers.PutController{})
}

one.tpl的代码和上面html的代码一样哦。

那么就这样简单用beego实现了一个上传文件的功能,但实际上,很多beego都封装好,只是等待你去调用这个方法。所以说,学习golang的基础很重要,如果golong里面没有,那么就自己写个封装好了。

最后一步,实现网页-golang服务器到七牛

这一步应该是最简单的一步,就是把前面的结合在一起,我就是把beego的代码和之前第一步写的代码合在一起就好了。因为这样做的话,以后扩展性会增强很多。

贴代码: 这里面的代码改变只是改变了default.go的代码,其他的代码没有什么改变。什么,为什么只改了这个的代码,因为这个是逻辑控制的部分。很好的把逻辑写在这里。控制逻辑的实现,上存的功能就是逻辑的实现。

package controllers

import (
   "github.com/astaxie/beego"
   "fmt"
   "qiniupkg.com/api.v7/kodo"
   "golang.org/x/net/context"
// "crypto/md5"
)

type MainController struct {
   beego.Controller
}
func init(){
   kodo.SetMac("从七牛获得的access key","从七牛获得的secret key")
   click=kodo.New(0,nil)
   bucket = click.Bucket("appfornew")
   ctx=context.Background()
   
}
var click *kodo.Client;
var bucket kodo.Bucket;
var ctx context.Context;
func (c *MainController) Get() {
   c.TplNames = "index.tpl"
}
type PutController struct{
   beego.Controller
}
func(c *PutController) Get(){
   c.TplNames ="test.tpl"
}
type EmailController struct{
   beego.Controller
}
func(c *PutController) Post(){
   fmt.Println("one")
   file,fileHead,err :=c.GetFile("uploadfile")
   fmt.Println("two")
   if err!=nil{
      fmt.Println(err)
      c.Ctx.WriteString("err")
      return
   }
   defer file.Close()
   fmt.Printf(fileHead.Filename)


   err = c.SaveToFile("uploadfile","./"+fileHead.Filename)
   if err!=nil{
      c.Ctx.WriteString("save to file err")
      fmt.Println(err)
   }else{
      localFile:=fileHead.Filename
      err:=bucket.PutFile(ctx,nil,fileHead.Filename,localFile,nil)
      if err!=nil{
      fmt.Println("has err")
      fmt.Println(err)
      c.Ctx.WriteString("err")
      return
   }else {
      c.Ctx.WriteString("success")
   }
   }
}

写到这里,好了,功能基本上是实现了。从网页上存到服务器然后服务器上存到七牛服务器。 每一次上存到golang服务器网页一次等待,golang服务器上存到七牛服务器,网页俩次等待,是不是觉得有点没效率呢?
所以就有了从客户端上存到七牛服务器,然后七牛服务器回调给golang服务器的方法。
那么下一步我可以做什么呢?那么就好好实现这个功能看看……当然,前提是我有了自己的服务器再说。