英文:
How to pass io.Reader instance to a function in Golang?
问题
你好!根据你提供的代码和问题,你需要将一个io.Reader
实例作为参数传递给一个由API提供的函数。你想知道如何正确地将io.Reader
实例传递给这个函数。
在你的代码中,你定义了一个var testIO io.Reader
变量,但是你没有给它赋值。在调用client.Files.Upload
函数时,你需要将testIO
作为第二个参数传递进去。
要正确地将io.Reader
实例传递给这个函数,你需要创建一个实现了io.Reader
接口的对象,并将其赋值给testIO
变量。根据你的需求,你可以使用os.Open
函数打开一个本地文件,并将返回的文件对象赋值给testIO
。
以下是一个示例代码片段,演示了如何将本地文件作为io.Reader
实例传递给client.Files.Upload
函数:
file, err := os.Open("path/to/local/file")
if err != nil {
log.Fatal(err)
}
defer file.Close()
var testIO io.Reader = file
upload, err := client.Files.Upload(context.TODO(), testIO, "test", 0)
if err != nil {
log.Fatal(err)
}
在这个示例中,我们使用os.Open
函数打开了一个本地文件,并将返回的文件对象赋值给testIO
变量。然后,我们将testIO
作为参数传递给client.Files.Upload
函数。
请根据你的实际需求修改文件路径,并确保在使用完文件后关闭文件。
希望这可以帮助到你!如果你有任何其他问题,请随时问。
英文:
I've been dealing with an issue where I have to put an io.Reader
instance as a parameter to a function provided as an end-point by an api. The task I need to do is to upload local folder to company's cloud storage.
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)
}
When I make a request to this end-point /upload
under POST method. I get the following error.
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
And this is the documentation of the function I am trying to use:
// 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
}
I am confused about putting the io.Reader
instance to a function. How can I put the io.Reader
function properly under this case. Just an instance after creating as follows:
var testIO io.Reader
or should I do some extra operations?
答案1
得分: 3
运行时错误:无效的内存地址或空指针解引用
正如你肯定知道的那样,这是因为你声明了一个io.Reader
,但你没有设置它的值,所以它仍然等于接口的默认值nil
。
将io.Reader
传递给Upload
的目的是提供要上传的数据。通过传递一个io.Reader
,任意数据源可以提供任意数量的字节,不受内存可用性的限制(不像[]byte
,它需要在上传之前将所有数据保存在内存中)。io.Reader
通常用于提供此类“流式”操作的数据。
io.Reader
应该是要上传的数据的来源。
io.Reader
可以是os.Open()
返回的文件。
但它可以是任何满足io.Reader
的东西-例如,它也可以是一个bytes.Buffer
。
它甚至可以是更奇特的东西,比如来自AWS流行的S3服务的GetObject
API调用的结果,它也返回满足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中最重要的概念之一。
英文:
runtime error: invalid memory address or nil pointer dereference
As you surely know, this is because you've declared an io.Reader
but you haven't set its value, so it is still equal to the default value of an interface, which is nil
.
var testIO io.Reader // ?
The point of passing the io.Reader
to Upload
is to provide the data to be uploaded. By passing an io.Reader
, an arbitrary source of data can provide an abitrary quantity of bytes, unrestricted by memory availability (unlike []byte
, which would require holding all data in memory before uploading). io.Reader
is commonly used for providing data for this kind of "streaming" operation.
Upload reads from given io.Reader and uploads the file contents
That io.Reader
should be the source of the data to upload.
io.Reader
could be a file from os.Open()
.
But it could be anything that satisfies io.Reader
- for example, it could also be a bytes.Buffer
.
It could even be something more esoteric, like the result of an GetObject
API call against the popular S3 service from AWS, which also returns an io.ReadCloser
which satisfies io.Reader
.
io.Reader
is a great example of how Go interfaces allow independent libraries to be connected to each other. the SDK you're using doesn't care what io.Reader
it's passed; it is enough that the value satisfies io.Reader
, a requirement enforced at compile time. You can pass it anything that satisfies io.Reader
, and the interface type guarantees that Upload()
will be able to handle it properly.
Upload
takes an io.Reader
. If you want to pass it something like an *os.File
from os.Open
or an io.ReadCloser
from say S3 GetObject, that works because *os.File
and io.ReadCloser
satisfy io.Reader
. But since Upload
takes io.Reader
, you can be confident that it will call nothing but the functions defined in io.Reader
. That means you'll have to do any closing yourself, after Upload
is called.
Make sure to take the time to understand how io.Reader
leaves the input to this function open-ended while also being specific about the interface it expects. This is one of the most important concepts in Go.
答案2
得分: 1
这段代码的翻译如下:
这段代码中:
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
方法)。
由于你正在上传一个文件,你的字节流应该来自于操作系统文件。os.File 实现了正确的 Read
方法,因此与 io.Reader
兼容。
所以尝试一下:
f, err := os.Open(uploadFilePath)
if err != nil { /* ... */ }
upload, err := client.Files.Upload(context.TODO(), f, "test", 0)
英文:
This:
var testIO io.Reader
is equivalent to this:
testIO := io.Reader(nil)
so this is why you are getting a panic of a nil-pointer reference:
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]:
An io.Reader is an interface which allows one to pass generic values, provided they implement the interface (i.e. implement the Read
method).
Since you are uploading a file, your byte stream should come from an OS file. os.File implements the correct Read
method - so is compatible io.Reader
.
So try:
f, err := os.Open(uploadFilePath)
if err != nil { /* ... */ }
upload, err := client.Files.Upload(context.TODO(), f, "test", 0)
答案3
得分: 1
当你定义一个变量时,它的初始值是该类型的“零值”。io.Reader
是一个接口,它的零值是nil
。因此会出现“空指针解引用错误”。在将其传递给Upload之前,只需初始化io.Reader
:
file, err := os.Open("path/to/file")
// if err != nil { ... }
upload, err := client.Files.Upload(context.TODO(), file, "test", 0)
英文:
When you define a variable, it is initial value is the zero value
for that type. io.Reader
is an interface and the zero value for it is nil
. Thus the nil pointer dereference error
. Simply initialize the io.Reader
before passing it to Upload:
file, err := os.Open("path/to/file")
// if err != nil { ... }
upload, err := client.Files.Upload(context.TODO(), file, "test", 0)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论