在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ GO/ 4.5 處理文件上傳
7 文本處理
3 Web基礎(chǔ)
14 擴展Web框架
10.4 小結(jié)
2.2 Go基礎(chǔ)
2.8 總結(jié)
6.1 session和cookie
5.5 使用beedb庫進行ORM開發(fā)
8.3 REST
13.6 小結(jié)
5.4 使用PostgreSQL數(shù)據(jù)庫
14.6 pprof支持
14.1 靜態(tài)文件支持
11.2 使用GDB調(diào)試
7.7 小結(jié)
1 GO環(huán)境配置
14.5 多語言支持
7.1 XML處理
1.5 總結(jié)
13 如何設(shè)計一個Web框架
14.3 表單及驗證支持
12 部署與維護
10 國際化和本地化
1.1 Go 安裝
6.2 Go如何使用session
5.6 NOSQL數(shù)據(jù)庫操作
6.5 小結(jié)
9.4 避免SQL注入
12.1 應(yīng)用日志
4.2 驗證表單的輸入
10.1 設(shè)置默認地區(qū)
1.3 Go 命令
9.6 加密和解密數(shù)據(jù)
4.1 處理表單的輸入
4.4 防止多次遞交表單
11.3 Go怎么寫測試用例
8 Web服務(wù)
12.3 應(yīng)用部署
5.7 小結(jié)
12.5 小結(jié)
11 錯誤處理,調(diào)試和測試
9.2 確保輸入過濾
14.2 Session支持
6.4 預(yù)防session劫持
12.4 備份和恢復(fù)
8.1 Socket編程
13.1 項目規(guī)劃
13.4 日志和配置設(shè)計
7.6 字符串處理
13.2 自定義路由器設(shè)計
6.3 session存儲
3.4 Go的http包詳解
8.2 WebSocket
10.3 國際化站點
7.5 文件操作
7.4 模板處理
9.1 預(yù)防CSRF攻擊
13.3 controller設(shè)計
2.6 interface
14.4 用戶認證
2.3 流程和函數(shù)
附錄A 參考資料
11.1 錯誤處理
9.5 存儲密碼
9.3 避免XSS攻擊
12.2 網(wǎng)站錯誤處理
6 session和數(shù)據(jù)存儲
2.4 struct類型
3.3 Go如何使得Web工作
2.5 面向?qū)ο?/span>
3.1 Web工作方式
1.2 GOPATH與工作空間
2.1 你好,Go
9.7 小結(jié)
13.5 實現(xiàn)博客的增刪改
7.2 JSON處理
10.2 本地化資源
7.3 正則處理
2 Go語言基礎(chǔ)
5.1 database/sql接口
4.5 處理文件上傳
8.5 小結(jié)
4.3 預(yù)防跨站腳本
5.3 使用SQLite數(shù)據(jù)庫
14.7 小結(jié)
3.2 Go搭建一個Web服務(wù)器
2.7 并發(fā)
5 訪問數(shù)據(jù)庫
4 表單
3.5 小結(jié)
1.4 Go開發(fā)工具
11.4 小結(jié)
9 安全與加密
5.2 使用MySQL數(shù)據(jù)庫
4.6 小結(jié)
8.4 RPC

4.5 處理文件上傳

你想處理一個由用戶上傳的文件,比如你正在建設(shè)一個類似Instagram的網(wǎng)站,你需要存儲用戶拍攝的照片。這種需求該如何實現(xiàn)呢?

要使表單能夠上傳文件,首先第一步就是要添加form的enctype屬性,enctype屬性有如下三種情況:

application/x-www-form-urlencoded   表示在發(fā)送前編碼所有字符(默認)
multipart/form-data   不對字符編碼。在使用包含文件上傳控件的表單時,必須使用該值。
text/plain    空格轉(zhuǎn)換為 "+" 加號,但不對特殊字符編碼。

所以,表單的html代碼應(yīng)該類似于:

<html>
<head>
    <title>上傳文件</title>
</head>
<body>
<form enctype="multipart/form-data" action="http://127.0.0.1:9090/upload" method="post">
  <input type="file" name="uploadfile" />
  <input type="hidden" name="token" value="{{.}}"/>
  <input type="submit" value="upload" />
</form>
</body>
</html>

在服務(wù)器端,我們增加一個handlerFunc:

http.HandleFunc("/upload", upload)

// 處理/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("upload.gtpl")
        t.Execute(w, token)
    } else {
        r.ParseMultipartForm(32 << 20)
        file, handler, err := r.FormFile("uploadfile")
        if err != nil {
            fmt.Println(err)
            return
        }
        defer file.Close()
        fmt.Fprintf(w, "%v", handler.Header)
        f, err := os.OpenFile("./test/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
        if err != nil {
            fmt.Println(err)
            return
        }
        defer f.Close()
        io.Copy(f, file)
    }
}

通過上面的代碼可以看到,處理文件上傳我們需要調(diào)用r.ParseMultipartForm,里面的參數(shù)表示maxMemory,調(diào)用ParseMultipartForm之后,上傳的文件存儲在maxMemory大小的內(nèi)存里面,如果文件大小超過了maxMemory,那么剩下的部分將存儲在系統(tǒng)的臨時文件中。我們可以通過r.FormFile獲取上面的文件句柄,然后實例中使用了io.Copy來存儲文件。

獲取其他非文件字段信息的時候就不需要調(diào)用r.ParseForm,因為在需要的時候Go自動會去調(diào)用。而且ParseMultipartForm調(diào)用一次之后,后面再次調(diào)用不會再有效果。

通過上面的實例我們可以看到我們上傳文件主要三步處理:

  1. 表單中增加enctype="multipart/form-data"
  2. 服務(wù)端調(diào)用r.ParseMultipartForm,把上傳的文件存儲在內(nèi)存和臨時文件中
  3. 使用r.FormFile獲取文件句柄,然后對文件進行存儲等處理。

文件handler是multipart.FileHeader,里面存儲了如下結(jié)構(gòu)信息

type FileHeader struct {
    Filename string
    Header   textproto.MIMEHeader
    // contains filtered or unexported fields
}

我們通過上面的實例代碼打印出來上傳文件的信息如下

http://wiki.jikexueyuan.com/project/go-web-programming/images/4.5.upload2.png?raw=true" alt="" />

圖4.5 打印文件上傳后服務(wù)器端接受的信息

客戶端上傳文件

我們上面的例子演示了如何通過表單上傳文件,然后在服務(wù)器端處理文件,其實Go支持模擬客戶端表單功能支持文件上傳,詳細用法請看如下示例:

package main

import (
    "bytes"
    "fmt"
    "io"
    "io/ioutil"
    "mime/multipart"
    "net/http"
    "os"
)

func postFile(filename string, targetUrl string) error {
    bodyBuf := &bytes.Buffer{}
    bodyWriter := multipart.NewWriter(bodyBuf)

    //關(guān)鍵的一步操作
    fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename)
    if err != nil {
        fmt.Println("error writing to buffer")
        return err
    }

    //打開文件句柄操作
    fh, err := os.Open(filename)
    if err != nil {
        fmt.Println("error opening file")
        return err
    }
    defer fh.Close()

    //iocopy
    _, err = io.Copy(fileWriter, fh)
    if err != nil {
        return err
    }

    contentType := bodyWriter.FormDataContentType()
    bodyWriter.Close()

    resp, err := http.Post(targetUrl, contentType, bodyBuf)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    resp_body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return err
    }
    fmt.Println(resp.Status)
    fmt.Println(string(resp_body))
    return nil
}

// sample usage
func main() {
    target_url := "http://localhost:9090/upload"
    filename := "./astaxie.pdf"
    postFile(filename, target_url)
}

上面的例子詳細展示了客戶端如何向服務(wù)器上傳一個文件的例子,客戶端通過multipart.Write把文件的文本流寫入一個緩存中,然后調(diào)用http的Post方法把緩存?zhèn)鞯椒?wù)器。

如果你還有其他普通字段例如username之類的需要同時寫入,那么可以調(diào)用multipart的WriteField方法寫很多其他類似的字段。

下一篇:14.2 Session支持