如何将 io.Reader 实例传递给 Golang 中的函数?
How to pass io.Reader instance to a function in Golang?
我一直在处理一个问题,我必须将一个 io.Reader
实例作为参数传递给由 api 作为端点提供的函数。我要做的任务是将本地文件夹上传到公司的云存储。
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
这是我尝试使用的函数的文档:
// 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
实例放入函数感到困惑。在这种情况下,我怎样才能正确地使用 io.Reader
函数。创建后的一个实例如下:
var testIO io.Reader
还是我应该做一些额外的操作?
这个:
var testIO io.Reader
相当于:
testIO := io.Reader(nil)
所以这就是为什么您对 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
方法)。
由于您正在上传文件,因此您的字节流应该来自 OS 文件。 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)
runtime error: invalid memory address or nil pointer dereference
你肯定知道,这是因为你声明了一个io.Reader
但是你没有设置它的值,所以它仍然等于接口的默认值,即[=14] =].
var testIO io.Reader // ?
将io.Reader
传递给Upload
的目的是提供要上传的数据。通过传递 io.Reader
,任意数据源可以提供任意数量的字节,不受内存可用性的限制(不像 []byte
,这需要在 之前将所有数据保存在内存中 上传)。 io.Reader
通常用于为这种“流式”操作提供数据。
Upload reads from given io.Reader and uploads the file contents
io.Reader
应该是上传数据的来源。
io.Reader
可能是来自 os.Open()
的文件。
但它可以是任何满足 io.Reader
的东西——例如,它也可以是 bytes.Buffer
.
它甚至可以是更深奥的东西,like the result of an GetObject
API call against the popular S3 service from AWS,它也是 returns 满足 io.Reader
.
的 io.ReadCloser
io.Reader
是 Go 接口如何允许独立库相互连接的一个很好的例子。您使用的 SDK 不关心它传递了什么 io.Reader
;该值满足 io.Reader
就足够了,这是编译时强制执行的要求。你可以传给它任何满足io.Reader
的,接口类型保证Upload()
能正常处理
Upload
取 io.Reader
。如果你想传递类似 os.Open
的 *os.File
或 S3 GetObject 的 io.ReadCloser
之类的东西,那是可行的,因为 *os.File
和 io.ReadCloser
满足 io.Reader
.但是由于 Upload
需要 io.Reader
,你可以确信它只会调用 io.Reader
中定义的函数。这意味着您必须在 Upload
被调用后自行关闭。
一定要花时间了解 io.Reader
如何让这个函数的输入保持开放状态,同时还要具体说明它期望的接口。这是 Go 中最重要的概念之一。
我一直在处理一个问题,我必须将一个 io.Reader
实例作为参数传递给由 api 作为端点提供的函数。我要做的任务是将本地文件夹上传到公司的云存储。
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
这是我尝试使用的函数的文档:
// 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
实例放入函数感到困惑。在这种情况下,我怎样才能正确地使用 io.Reader
函数。创建后的一个实例如下:
var testIO io.Reader
还是我应该做一些额外的操作?
这个:
var testIO io.Reader
相当于:
testIO := io.Reader(nil)
所以这就是为什么您对 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
方法)。
由于您正在上传文件,因此您的字节流应该来自 OS 文件。 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)
runtime error: invalid memory address or nil pointer dereference
你肯定知道,这是因为你声明了一个io.Reader
但是你没有设置它的值,所以它仍然等于接口的默认值,即[=14] =].
var testIO io.Reader // ?
将io.Reader
传递给Upload
的目的是提供要上传的数据。通过传递 io.Reader
,任意数据源可以提供任意数量的字节,不受内存可用性的限制(不像 []byte
,这需要在 之前将所有数据保存在内存中 上传)。 io.Reader
通常用于为这种“流式”操作提供数据。
Upload reads from given io.Reader and uploads the file contents
io.Reader
应该是上传数据的来源。
io.Reader
可能是来自 os.Open()
的文件。
但它可以是任何满足 io.Reader
的东西——例如,它也可以是 bytes.Buffer
.
它甚至可以是更深奥的东西,like the result of an GetObject
API call against the popular S3 service from AWS,它也是 returns 满足 io.Reader
.
io.ReadCloser
io.Reader
是 Go 接口如何允许独立库相互连接的一个很好的例子。您使用的 SDK 不关心它传递了什么 io.Reader
;该值满足 io.Reader
就足够了,这是编译时强制执行的要求。你可以传给它任何满足io.Reader
的,接口类型保证Upload()
能正常处理
Upload
取 io.Reader
。如果你想传递类似 os.Open
的 *os.File
或 S3 GetObject 的 io.ReadCloser
之类的东西,那是可行的,因为 *os.File
和 io.ReadCloser
满足 io.Reader
.但是由于 Upload
需要 io.Reader
,你可以确信它只会调用 io.Reader
中定义的函数。这意味着您必须在 Upload
被调用后自行关闭。
一定要花时间了解 io.Reader
如何让这个函数的输入保持开放状态,同时还要具体说明它期望的接口。这是 Go 中最重要的概念之一。