fix(desktop): 原生下载失败会在用户选定路径上留半截文件
桌面端 Go 侧此前零测试。补 5 个(download/ReadLocalFile/Ping), 逮到一个真 bug:io.Copy 中途断开时,用户选定的路径上会留下一个半截的 .docx —— 带着用户自己起的名字躺在那儿,虽然前端会弹错误,但以后双击打不开, 而用户会以为是导出功能坏了。现在失败一律 os.Remove 不留残file。 同批修的两处(同一段代码,都没测试盖到): - Close 的错误被 defer 吞掉。写文件时 io.Copy 成功不代表数据落盘, flush 失败只在 Close 上报——吞掉就是静默截断,且 download 返回 nil(成功)。 - http.Get 用默认 client,没有超时。上游卡住的话「另存为」会永远转, 用户只能强杀 app。改用带 3 分钟超时的 client。 顺带核实过一个可疑点、结论是不用改:download 走裸 http.Get 不带鉴权头, 但报告导出路由 `/reports/:id/export` 是故意公开的(router.go 注释: "EventSource/下载无法带 Bearer"),所以能通。 对话框本身(application.Get().Dialog)要真窗口,自动化盖不到,仍需手点。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+20
-3
@@ -8,6 +8,7 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
goruntime "runtime"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
@@ -68,8 +69,14 @@ func (a *App) Notify(title, body string) {
|
||||
}
|
||||
}
|
||||
|
||||
func download(url, dst string) error {
|
||||
resp, err := http.Get(url)
|
||||
// downloadClient 给下载加超时:默认 http.Get 用的 client 没有超时,
|
||||
// 上游卡住的话「另存为」会永远转下去,用户只能强杀 app。
|
||||
var downloadClient = &http.Client{Timeout: 3 * time.Minute}
|
||||
|
||||
// download 把 url 下载到 dst。失败一律不留残file:
|
||||
// 中途断开会在用户选定的路径上留个半截 .docx,带着用户起的名字,以后双击打不开。
|
||||
func download(url, dst string) (err error) {
|
||||
resp, err := downloadClient.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -81,7 +88,17 @@ func download(url, dst string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
defer func() {
|
||||
// Close 的错误不能吞:写文件时 io.Copy 成功不代表数据落了盘,
|
||||
// flush 失败只会在 Close 上报出来——吞掉就是静默截断。
|
||||
cerr := f.Close()
|
||||
if err == nil {
|
||||
err = cerr
|
||||
}
|
||||
if err != nil {
|
||||
_ = os.Remove(dst) // 失败不留残file
|
||||
}
|
||||
}()
|
||||
_, err = io.Copy(f, resp.Body)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user