mirror of https://gitee.com/godoos/godoos.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
156 lines
3.9 KiB
156 lines
3.9 KiB
package store
|
|
|
|
import (
|
|
"encoding/json"
|
|
"godo/files"
|
|
"godo/libs"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
)
|
|
|
|
type ProgressReader struct {
|
|
reader io.Reader
|
|
total int64
|
|
err error
|
|
}
|
|
|
|
type DownloadStatus struct {
|
|
Name string `json:"name"`
|
|
Path string `json:"path"`
|
|
Url string `json:"url"`
|
|
Current int64 `json:"current"`
|
|
Size int64 `json:"size"`
|
|
Speed float64 `json:"speed"`
|
|
Progress int `json:"progress"`
|
|
Downloading bool `json:"downloading"`
|
|
Done bool `json:"done"`
|
|
}
|
|
|
|
func (pr *ProgressReader) Read(p []byte) (n int, err error) {
|
|
n, err = pr.reader.Read(p)
|
|
pr.err = err
|
|
pr.total += int64(n)
|
|
return
|
|
}
|
|
|
|
func DownloadHandler(w http.ResponseWriter, r *http.Request) {
|
|
url := r.URL.Query().Get("url")
|
|
log.Printf("Download url: %s", url)
|
|
|
|
// 获取下载目录,这里假设从请求参数中获取,如果没有则使用默认值
|
|
downloadDir := libs.GetCacheDir()
|
|
if downloadDir == "" {
|
|
downloadDir = "./downloads"
|
|
}
|
|
|
|
// 拼接完整的文件路径
|
|
fileName := filepath.Base(url)
|
|
filePath := filepath.Join(downloadDir, fileName)
|
|
|
|
// 开始下载
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
log.Printf("Failed to get file: %v", err)
|
|
http.Error(w, "Failed to get file", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// 检查文件是否已存在且大小一致
|
|
if fileInfo, err := os.Stat(filePath); err == nil {
|
|
if fileInfo.Size() == resp.ContentLength {
|
|
// 文件已存在且大小一致,无需下载
|
|
runDir := libs.GetRunDir()
|
|
err := files.HandlerFile(filePath, runDir)
|
|
if err != nil {
|
|
log.Printf("Error moving file: %v", err)
|
|
}
|
|
libs.SuccessMsg(w, "success", "File already exists and is of correct size")
|
|
return
|
|
} else {
|
|
// 重新打开响应体以便后续读取
|
|
resp.Body = http.NoBody
|
|
resp, err = http.Get(url)
|
|
if err != nil {
|
|
log.Printf("Failed to get file: %v", err)
|
|
http.Error(w, "Failed to get file", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// 创建文件
|
|
file, err := os.Create(filePath)
|
|
if err != nil {
|
|
log.Printf("Failed to create file: %v", err)
|
|
http.Error(w, "Failed to create file", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
// 使用ProgressReader来跟踪进度
|
|
pr := &ProgressReader{reader: resp.Body}
|
|
|
|
// 启动定时器来报告进度
|
|
ticker := time.NewTicker(200 * time.Millisecond)
|
|
defer ticker.Stop()
|
|
|
|
flusher, ok := w.(http.Flusher)
|
|
if !ok {
|
|
log.Printf("Streaming unsupported")
|
|
http.Error(w, "Streaming unsupported", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
go func() {
|
|
for {
|
|
<-ticker.C
|
|
rp := &DownloadStatus{
|
|
Name: fileName,
|
|
Path: filePath,
|
|
Url: url,
|
|
Current: pr.total,
|
|
Size: resp.ContentLength,
|
|
Speed: 0, // 这里可以计算速度,但为了简化示例,我们暂时设为0
|
|
Progress: int(100 * (float64(pr.total) / float64(resp.ContentLength))),
|
|
Downloading: pr.err == nil && pr.total < resp.ContentLength,
|
|
Done: pr.total == resp.ContentLength,
|
|
}
|
|
if pr.err != nil || rp.Done {
|
|
rp.Downloading = false
|
|
//log.Printf("Download complete: %s", filePath)
|
|
runDir := libs.GetRunDir()
|
|
err := files.HandlerFile(filePath, runDir)
|
|
if err != nil {
|
|
log.Printf("Error moving file: %v", err)
|
|
}
|
|
break
|
|
}
|
|
if w != nil {
|
|
jsonBytes, err := json.Marshal(rp)
|
|
if err != nil {
|
|
log.Printf("Error marshaling DownloadStatus to JSON: %v", err)
|
|
continue
|
|
}
|
|
w.Write(jsonBytes)
|
|
w.Write([]byte("\n"))
|
|
flusher.Flush()
|
|
} else {
|
|
log.Println("ResponseWriter is nil, cannot send progress")
|
|
}
|
|
}
|
|
}()
|
|
|
|
// 将响应体的内容写入文件
|
|
_, err = io.Copy(file, pr)
|
|
if err != nil {
|
|
log.Printf("Failed to write file: %v", err)
|
|
http.Error(w, "Failed to write file", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
libs.SuccessMsg(w, "success", "Download complete")
|
|
}
|
|
|