我一直在閱讀關於http請求上可用的各種超時的問題,並且他們似乎都充當請求總時間的嚴格最後期限。如何在http下載中實現不活動超時
我正在運行http下載,我不想在初始握手後執行硬超時,因爲我對用戶連接不瞭解,也不想在慢速連接上超時。我最理想的是在一段時間不活動後超時(當x秒沒有下載任何東西時)。有沒有辦法做到這一點作爲一個內置的,或者我必須中斷的基礎上說明文件?
工作代碼有點難以分離,但我認爲這些是相關的部分,還有另一個循環來統計文件以提供進度,但是我需要重構一下來使用它來中斷下載:
// httspClientOnNetInterface returns an http client using the named network interface, (via proxy if passed)
func HttpsClientOnNetInterface(interfaceIP []byte, httpsProxy *Proxy) (*http.Client, error) {
log.Printf("Got IP addr : %s\n", string(interfaceIP))
// create address for the dialer
tcpAddr := &net.TCPAddr{
IP: interfaceIP,
}
// create the dialer & transport
netDialer := net.Dialer{
LocalAddr: tcpAddr,
}
var proxyURL *url.URL
var err error
if httpsProxy != nil {
proxyURL, err = url.Parse(httpsProxy.String())
if err != nil {
return nil, fmt.Errorf("Error parsing proxy connection string: %s", err)
}
}
httpTransport := &http.Transport{
Dial: netDialer.Dial,
Proxy: http.ProxyURL(proxyURL),
}
httpClient := &http.Client{
Transport: httpTransport,
}
return httpClient, nil
}
/*
StartDownloadWithProgress will initiate a download from a remote url to a local file,
providing download progress information
*/
func StartDownloadWithProgress(interfaceIP []byte, httpsProxy *Proxy, srcURL, dstFilepath string) (*Download, error) {
// start an http client on the selected net interface
httpClient, err := HttpsClientOnNetInterface(interfaceIP, httpsProxy)
if err != nil {
return nil, err
}
// grab the header
headResp, err := httpClient.Head(srcURL)
if err != nil {
log.Printf("error on head request (download size): %s", err)
return nil, err
}
// pull out total size
size, err := strconv.Atoi(headResp.Header.Get("Content-Length"))
if err != nil {
headResp.Body.Close()
return nil, err
}
headResp.Body.Close()
errChan := make(chan error)
doneChan := make(chan struct{})
// spawn the download process
go func(httpClient *http.Client, srcURL, dstFilepath string, errChan chan error, doneChan chan struct{}) {
resp, err := httpClient.Get(srcURL)
if err != nil {
errChan <- err
return
}
defer resp.Body.Close()
// create the file
outFile, err := os.Create(dstFilepath)
if err != nil {
errChan <- err
return
}
defer outFile.Close()
log.Println("starting copy")
// copy to file as the response arrives
_, err = io.Copy(outFile, resp.Body)
// return err
if err != nil {
log.Printf("\n Download Copy Error: %s \n", err.Error())
errChan <- err
return
}
doneChan <- struct{}{}
return
}(httpClient, srcURL, dstFilepath, errChan, doneChan)
// return Download
return (&Download{
updateFrequency: time.Microsecond * 500,
total: size,
errRecieve: errChan,
doneRecieve: doneChan,
filepath: dstFilepath,
}).Start(), nil
}
更新 謝謝大家誰已經輸入到這一點。
我接受了JimB的答案,因爲它似乎是一種比我選擇的解決方案更普遍的完全可行的方法(並且可能對任何在這裏找到方法的人更有用)。
在我的情況下,我已經有一個循環監控文件的大小,所以當我在x秒內沒有改變時,我拋出了一個命名錯誤。我通過現有的錯誤處理獲取指定的錯誤並從那裏重試下載更容易。
我可能會崩潰至少一個夠程與我的做法的背景(我以後可能會用一些信令解決這個問題),但因爲這是一個短期運行的應用程序(它的安裝程序),所以這是可以接受的(至少是容忍)
你可以用你自己寫的東西來替代'io.Copy',它爲每個單獨的'Read'調用設置一個超時時間,並且通過向某個通道寫入某些東西,甚至可以通知您目前複製的數據量。 – Krom
這不是一個糟糕的解決方案,感覺比我的計劃更清潔 - 感謝您的建議 – WebweaverD
被警告,更換'io.Copy'要複雜得多,然後人們會認爲,甚至有競爭要做,而獲得最多不錯的屬性:[可能是相關的](https://groups.google。com/d/msg/golang-nuts/434c3YInH_M/3Qd7C0uDUqQJ) – Krom