如何将 io.Reader 实例传递给 Golang 中的函数?

huangapple go评论79阅读模式
英文:

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.Readerio.ReadCloser

io.Reader是Go接口如何连接独立库的很好的例子。你使用的SDK不关心它被传递的io.Reader是什么;它只要求该值满足io.Reader,这是在编译时强制执行的要求。你可以传递任何满足io.Reader的东西,接口类型保证Upload()将能够正确处理它。

Upload接受一个io.Reader。如果你想传递一个来自os.Open*os.File或来自S3 GetObject的io.ReadCloser,那是可以的,因为*os.Fileio.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)

huangapple
  • 本文由 发表于 2021年12月1日 23:32:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/70186984.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定