簡體   English   中英

如何將 io.Reader 實例傳遞給 Golang 中的 function?

[英]How to pass io.Reader instance to a function in Golang?

我一直在處理一個問題,我必須將io.Reader實例作為參數放入由 api 作為端點提供的 function。 我需要做的任務是將本地文件夾上傳到公司的雲存儲。

func (s *server) uploadFileToPutIo(w http.ResponseWriter, r *http.Request) {
    tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
    oauthClient := oauth2.NewClient(context.TODO(), tokenSource)
    client := putio.NewClient(oauthClient)

    var testIO io.Reader // ? 

    upload, err := client.Files.Upload(context.TODO(), testIO, "test", 0)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(upload.File)
    sendResponse(w, []byte("successful"), http.StatusOK)
}

當我在 POST 方法下向這個端點/upload發出請求時。 我收到以下錯誤。

2021/12/01 18:28:47 http: panic serving 127.0.0.1:61057: runtime error: invalid memory address or nil pointer dereference
goroutine 8 [running]:
net/http.(*conn).serve.func1(0xc000108d20)
        /usr/local/Cellar/go/1.16.6/libexec/src/net/http/server.go:1804 +0x153
panic(0x1390ae0, 0x164fdd0)
        /usr/local/Cellar/go/1.16.6/libexec/src/runtime/panic.go:971 +0x499
io.copyBuffer(0x1462700, 0xc000026360, 0x0, 0x0, 0xc000170000, 0x8000, 0x8000, 0x0, 0x0, 0x13d5e01)
        /usr/local/Cellar/go/1.16.6/libexec/src/io/io.go:423 +0x10b
io.Copy(...)
        /usr/local/Cellar/go/1.16.6/libexec/src/io/io.go:382
github.com/putdotio/go-putio/putio.(*FilesService).Upload(0xc000010088, 0x1468390, 0xc00001c088, 0x0, 0x0, 0x13ef46f, 0x6, 0x0, 0x170d108, 0x90, ...)
        /Users/barisertas/go/pkg/mod/github.com/putdotio/go-putio/putio@v0.0.0-20200123120452-16d982cac2b8/files.go:235 +0x187
main.(*server).uploadFileToPutIo(0xc000010028, 0x1467d60, 0xc00014a2a0, 0xc000154500)
        /Users/barisertas/workspace/mini-project/api/handler.go:79 +0xe5
net/http.HandlerFunc.ServeHTTP(0xc000012db0, 0x1467d60, 0xc00014a2a0, 0xc000154500)
        /usr/local/Cellar/go/1.16.6/libexec/src/net/http/server.go:2049 +0x44
github.com/gorilla/mux.(*Router).ServeHTTP(0xc000144000, 0x1467d60, 0xc00014a2a0, 0xc000154300)
        /Users/barisertas/go/pkg/mod/github.com/gorilla/mux@v1.8.0/mux.go:210 +0xd3
net/http.serverHandler.ServeHTTP(0xc00014a000, 0x1467d60, 0xc00014a2a0, 0xc000154300)
        /usr/local/Cellar/go/1.16.6/libexec/src/net/http/server.go:2867 +0xa3
net/http.(*conn).serve(0xc000108d20, 0x1468400, 0xc00005e300)
        /usr/local/Cellar/go/1.16.6/libexec/src/net/http/server.go:1932 +0x8cd
created by net/http.(*Server).Serve
        /usr/local/Cellar/go/1.16.6/libexec/src/net/http/server.go:2993 +0x39b
2021/12/01 18:28:47 http: panic serving 127.0.0.1:61059: runtime error: invalid memory address or nil pointer dereference
goroutine 11 [running]:
net/http.(*conn).serve.func1(0xc000108dc0)
        /usr/local/Cellar/go/1.16.6/libexec/src/net/http/server.go:1804 +0x153
panic(0x1390ae0, 0x164fdd0)
        /usr/local/Cellar/go/1.16.6/libexec/src/runtime/panic.go:971 +0x499
io.copyBuffer(0x1462700, 0xc000026400, 0x0, 0x0, 0xc000198000, 0x8000, 0x8000, 0x0, 0x0, 0x13d5e01)
        /usr/local/Cellar/go/1.16.6/libexec/src/io/io.go:423 +0x10b
io.Copy(...)
        /usr/local/Cellar/go/1.16.6/libexec/src/io/io.go:382
github.com/putdotio/go-putio/putio.(*FilesService).Upload(0xc0000100c8, 0x1468390, 0xc00001c088, 0x0, 0x0, 0x13ef46f, 0x6, 0x0, 0x170d108, 0x90, ...)
        /Users/barisertas/go/pkg/mod/github.com/putdotio/go-putio/putio@v0.0.0-20200123120452-16d982cac2b8/files.go:235 +0x187
main.(*server).uploadFileToPutIo(0xc000010028, 0x1467d60, 0xc00014a380, 0xc000154800)
        /Users/barisertas/workspace/mini-project/api/handler.go:79 +0xe5
net/http.HandlerFunc.ServeHTTP(0xc000012db0, 0x1467d60, 0xc00014a380, 0xc000154800)
        /usr/local/Cellar/go/1.16.6/libexec/src/net/http/server.go:2049 +0x44
github.com/gorilla/mux.(*Router).ServeHTTP(0xc000144000, 0x1467d60, 0xc00014a380, 0xc000154600)
        /Users/barisertas/go/pkg/mod/github.com/gorilla/mux@v1.8.0/mux.go:210 +0xd3
net/http.serverHandler.ServeHTTP(0xc00014a000, 0x1467d60, 0xc00014a380, 0xc000154600)
        /usr/local/Cellar/go/1.16.6/libexec/src/net/http/server.go:2867 +0xa3
net/http.(*conn).serve(0xc000108dc0, 0x1468400, 0xc00005e580)
        /usr/local/Cellar/go/1.16.6/libexec/src/net/http/server.go:1932 +0x8cd
created by net/http.(*Server).Serve
        /usr/local/Cellar/go/1.16.6/libexec/src/net/http/server.go:2993 +0x39b

這是我正在嘗試使用的 function 的文檔:

// Upload reads from given io.Reader and uploads the file contents to Put.io
// servers under directory given by parent. If parent is negative, user's
// preferred folder is used.
//
// If the uploaded file is a torrent file, Put.io will interpret it as a
// transfer and Transfer field will be present to represent the status of the
// tranfer. Likewise, if the uploaded file is a regular file, Transfer field
// would be nil and the uploaded file will be represented by the File field.
//
// This method reads the file contents into the memory, so it should be used for
// <150MB files.
func (f *FilesService) Upload(ctx context.Context, r io.Reader, filename string, parent int64) (Upload, error) {
    if filename == "" {
        return Upload{}, fmt.Errorf("filename cannot be empty")
    }

    var buf bytes.Buffer
    mw := multipart.NewWriter(&buf)

    // negative parent means use user's preferred download folder.
    if parent >= 0 {
        err := mw.WriteField("parent_id", itoa(parent))
        if err != nil {
            return Upload{}, err
        }
    }

    formfile, err := mw.CreateFormFile("file", filename)
    if err != nil {
        return Upload{}, err
    }

    _, err = io.Copy(formfile, r)
    if err != nil {
        return Upload{}, err
    }

    err = mw.Close()
    if err != nil {
        return Upload{}, err
    }

    req, err := f.client.NewRequest(ctx, "POST", "/v2/files/upload", &buf)
    if err != nil {
        return Upload{}, err
    }
    req.Header.Set("Content-Type", mw.FormDataContentType())

    var response struct {
        Upload
    }
    _, err = f.client.Do(req, &response)
    if err != nil {
        return Upload{}, err
    }
    return response.Upload, nil
}

我對將io.Reader實例放入 function 感到困惑。 在這種情況下,如何正確放置io.Reader function。 創建后只是一個實例如下: var testIO io.Reader還是我應該做一些額外的操作?

runtime error: invalid memory address or nil pointer dereference

你肯定知道,這是因為你聲明了一個io.Reader但你沒有設置它的值,所以它仍然等於接口的默認值,即nil

    var testIO io.Reader // ? 

io.Reader傳遞給Upload的目的是提供要上傳的數據。 通過傳遞io.Reader ,任意數據源可以提供任意數量的字節,不受 memory 可用性的限制(與[]byte不同,在上傳之前需要將所有數據保存在 ZCD69B4957F06CDD818D17 中) io.Reader通常用於為這種“流式傳輸”操作提供數據。

Upload reads from given io.Reader and uploads the file contents

io.Reader應該是要上傳的數據的來源。

io.Reader可能是來自os.Open()的文件。

但它可以是任何滿足io.Reader的東西 - 例如,它也可以是bytes.Buffer

它甚至可能是更深奧的東西, 例如GetObject API 調用來自 AWS 的流行 S3 服務的結果,它還返回滿足io.ReadCloserio.Reader

io.Reader是 Go 接口如何允許獨立庫相互連接的一個很好的例子。 您使用的 SDK 不在乎io.Reader它通過了什么; 該值滿足io.Reader就足夠了,這是在編譯時強制執行的要求。 您可以將任何滿足io.Reader的內容傳遞給它,並且接口類型保證Upload()能夠正確處理它。

Upload需要io.Reader 如果你想通過 os.Open 的*os.File os.Open或 S3 GetObject 的io.ReadCloser之類的東西,這是可行的,因為io.ReadCloser滿足io.Reader 但是由於Upload需要io.Reader而不是io.ReadCloser您可以確信它只會調用該接口中定義的函數。 在調用Upload之后,您必須自己關閉。

請務必花時間了解io.Reader如何將輸入留給此 function 開放式,同時還要具體說明它所期望的接口。 這是 Go 中最重要的概念之一。

這個:

var testIO io.Reader

相當於:

testIO := io.Reader(nil)

所以這就是為什么你對零指針引用感到恐慌的原因:

2021/12/01 18:28:47 http: panic serving 127.0.0.1:61059: runtime error: invalid memory address or nil pointer dereference
goroutine 11 [running]:

io.Reader是一個允許傳遞通用值的接口,只要它們實現該接口(即實現Read方法)。

由於您正在上傳文件,因此您的字節 stream 應該來自操作系統文件。 os.File實現了正確的Read方法 - 因此兼容io.Reader

所以試試:

f, err := os.Open(uploadFilePath)
if err != nil { /* ... */ }

upload, err := client.Files.Upload(context.TODO(), f, "test", 0)

當您定義一個變量時,它的初始值是該類型的zero value io.Reader是一個接口,它的零值為nil 因此nil pointer dereference error 在將io.Reader傳遞給 Upload 之前,只需初始化它:

file, err := os.Open("path/to/file")
// if err != nil { ... }

upload, err := client.Files.Upload(context.TODO(), file, "test", 0)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM