英文:
How do I handle modified time with http.ServeContent()?
问题
我正在尝试使用ServeContent来提供文件(可能是大型电影文件,因此会使用字节范围),但我不确定如何处理修改时间。如果我使用以下程序来提供电影,如果我给出文件的实际修改时间,它会失败。我认为问题在于第一个请求成功,但后续的请求(文件的不同字节范围)认为它已经有了文件,因此它们失败,电影无法播放。我是否做错了什么?
请注意,如果我使用time.Now()而不是文件的实际修改时间,代码可以正常工作(电影可以正确播放),但这当然是不正确的。
package main
import (
"fmt"
"net/http"
"os"
"path"
"time"
)
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":3000", nil)
}
func handler(w http.ResponseWriter, r *http.Request) {
filePath := "." + r.URL.Path
file, err := os.Open(filePath)
if err != nil {
fmt.Printf("%s not found\n", filePath)
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "<html><body style='font-size:100px'>four-oh-four</body></html>")
return
}
defer file.Close()
fileStat, err := os.Stat(filePath)
if err != nil {
fmt.Println(err)
}
fmt.Printf("serve %s\n", filePath)
_, filename := path.Split(filePath)
t := fileStat.ModTime()
fmt.Printf("time %+v\n", t)
http.ServeContent(w, r, filename, t, file)
}
以上是你提供的代码。
英文:
I'm trying to use ServeContent to serve files (which may be large movie files, so it will use byte ranges), but I'm not sure how to handle the modified time. If I use the following program to serve a movie, it fails if I give the actual modified time of the file as shown. I think what happens is that the first request works, but subsequent ones (of different byte ranges of the file) think it already has the file and therefore they fail and the movie doesn't play. Is there something I am doing wrong?
Note that the code works (and the movie plays properly) if I use time.Now() instead of the actual modified time of the file, but that isn't correct of course.
package main
import (
"fmt"
"net/http"
"os"
"path"
"time"
)
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":3000", nil)
}
func handler(w http.ResponseWriter, r *http.Request) {
filePath := "." + r.URL.Path
file, err := os.Open(filePath)
if err != nil {
fmt.Printf("%s not found\n", filePath)
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "<html><body style='font-size:100px'>four-oh-four</body></html>")
return
}
defer file.Close()
fileStat, err := os.Stat(filePath)
if err != nil {
fmt.Println(err)
}
fmt.Printf("serve %s\n", filePath)
_, filename := path.Split(filePath)
t := fileStat.ModTime()
fmt.Printf("time %+v\n", t)
http.ServeContent(w, r, filename, t, file)
}
答案1
得分: 4
根据文档,
> 如果modtime不是零时间,则ServeContent会在响应中包含一个Last-Modified头。如果请求包含一个If-Modified-Since头,ServeContent会使用modtime来决定是否需要发送内容。
因此,根据客户端是否发送了If-Modified-Since
头,该函数将正确或不正确地执行。这似乎是预期的行为,在正常情况下对于优化服务器带宽非常有用。
然而,在你的情况下,由于你必须处理部分内容请求,除非第一个请求返回一个30X的HTTP代码,否则你没有理由处理后续请求的这个机制。
禁用这种行为的正确方法是向ServeContent
传递一个“零”日期:
http.ServeContent(w, r, filename, time.Time{}, file)
你可以尝试解析请求的范围头,以便只在必要时传递一个零日期。
英文:
According to the documentation,
> If modtime is not the zero time, ServeContent includes it in a Last-Modified header in the response. If the request includes an If-Modified-Since header, ServeContent uses modtime to decide whether the content needs to be sent at all.
So, depending on whether the client sends the If-Modified-Since
header, this function will behave correctly or not. This seems to be the intended behaviour, and is indeed useful in normal situations to optimize the server's bandwidth.
In your case, however, as you have to handle partial-content requests, unless the first request returns a 30X HTTP code, you have no reason to handle this mechanism for subsequent requests.
The correct way to disable this behaviour is to pass a "zero" date to ServeContent
:
http.ServeContent(w, r, filename, time.Time{}, file)
You could try to parse the request range header in order to only pass a zero date if necessary.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论