使用 Go 在 Google App Engine 中读取本地文件

3

我正在尝试在Google应用引擎上使用Go而不是Python来构建我的网站。但是当我本地测试脚本时,一直出现以下错误。

panic: runtime error: invalid memory address or nil pointer dereference

我很困惑,但是如果我注释掉它,它将不会出错。

channel <- buffer[0:dat]

我一定是在错误使用通道,有什么帮助吗?

编辑:

这是可行的代码,非常感谢Kevin Ballard帮助我解决了这个问题。

package defp

import (
    "fmt"
    "http"
    "os"
)

func getContent(filename string, channel chan []byte) {
    file, err := os.OpenFile(filename, os.O_RDONLY, 0666)
    defer file.Close()
    if err == nil {
        fmt.Printf("FILE FOUND : " + filename + " \n")
        buffer := make([]byte, 16)
        dat, err := file.Read(buffer)
        for err == nil {
            fmt.Printf("herp")
            channel <- buffer[0:dat]
            buffer = make([]byte, 16)
            dat, err = file.Read(buffer)
        }
        close(channel)
        fmt.Printf("DONE READING\n")
    } else {
        fmt.Printf("FILE NOT FOUND : " + filename + " \n")
    }
}
func writeContent(w http.ResponseWriter, channel chan []byte) {
    fmt.Printf("ATTEMPTING TO WRITE CONTENT\n")
    go func() {
        for bytes := range channel {
            w.Write(bytes)
            fmt.Printf("BYTES RECEIVED\n")
        }
    }()
    fmt.Printf("FINISHED WRITING\n")
}
func load(w http.ResponseWriter, path string) {
    fmt.Printf("ATTEMPTING LOAD " + path + "\n")
    channel := make(chan []byte, 50)
    writeContent(w, channel)
    getContent(path, channel)
}
func handle(w http.ResponseWriter, r *http.Request) {
    fmt.Printf("HANDLING REQUEST FOR " + r.URL.Path[1:] + "\n")
    load(w, r.URL.Path[1:])
}
func init() {
    http.HandleFunc("/", handle)
}

1
恐慌应该给你一个回溯。它实际上是在哪一行引发了恐慌? - Lily Ballard
1
我尝试在本地运行您的代码,但它没有崩溃。当然,它实际上也没有工作,但这是因为a)您正在尝试在goroutine中编写响应而不是在处理程序中同步(只需在“writeContent”之前去掉“go”),并且b)您正在将单个字节写为数字,而不是将字节作为字节写入(将“writeContent”更改为仅使用“w.Write(bytes)”)。即使有了这个,您仍将面临线程问题,因为您正在重用发送到其他通道的缓冲区。 - Lily Ballard
1
是的,我确实想把 fmt.Fprint(w, bytes) 改成 w.Write(bytes)。前者将字节数组作为整数序列写入,而后者直接将其作为字节写入。 - Lily Ballard
1
你仍然在一个goroutine中从通道读取数据。我没有深入研究http包的实际实现,但通过实验发现,一旦你从处理函数返回,响应就会被写入到网络中。通过在goroutine中从通道读取数据,你实际上是在已经发送完毕后才写入响应,所以无法正常工作。你应该移除包裹在读取循环周围的go func()。你还需要在打印“DONE READING”的地方调用close()方法,否则读取循环将永远无法终止。 - Lily Ballard
啊,是的,我刚刚完成了关闭的事情,准备更新 :)。至于杀死go func(),它完美地工作了:D。 - seveibar
显示剩余2条评论
1个回答

5
你的程序有时会出现崩溃的原因是有时候在程序退出load函数后,仍然会向w http.ResponseWriter写入数据。当程序退出句柄函数时,http包会自动关闭http.ResponseWriter。在writeContent函数中,程序有时会尝试向已关闭的http.ResponseWriter写入数据。

顺便提一下:如果使用io.Copy函数,可以使程序源代码更加简洁。

为了始终获得可预测的行为,请确保在退出句柄函数之前完成所有希望程序响应HTTP请求执行的工作。例如:

func writeContent(w http.ResponseWriter, channel chan []byte) {
    fmt.Printf("ATTEMPTING TO WRITE CONTENT\n")
    for bytes := range channel {
            w.Write(bytes)
            fmt.Printf("BYTES RECEIVED\n")
    }
    fmt.Printf("FINISHED WRITING\n")
}

func load(w http.ResponseWriter, path string) {
    fmt.Printf("ATTEMPTING LOAD " + path + "\n")
    channel := make(chan []byte)
    workDone := make(chan byte)
    go func() {
            writeContent(w, channel)
            workDone <- 1 //Send an arbitrary value
    }()
    go func() {
            getContent(path, channel)
            workDone <- 2 //Send an arbitrary value
    }()
    <-workDone
    <-workDone
}

func handle(w http.ResponseWriter, r *http.Request) {
    fmt.Printf("HANDLING REQUEST FOR " + r.URL.Path[1:] + "\n")
    load(w, r.URL.Path[1:])
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接