@@ -17,6 +17,7 @@ import (
1717 filev1 "github.com/rtmelsov/adv-keeper/gen/go/proto/file/v1"
1818 "github.com/rtmelsov/adv-keeper/internal/helpers"
1919 "github.com/rtmelsov/adv-keeper/internal/middleware"
20+ "github.com/rtmelsov/adv-keeper/internal/models"
2021 "google.golang.org/grpc/codes"
2122 "google.golang.org/grpc/status"
2223)
@@ -43,15 +44,17 @@ func safeBase(name string) string {
4344 }
4445 return string (runes )
4546}
46-
47- func DownloadFile ( fileID string ) ( * filev1. GetFilesResponse , error ) {
47+ func DownloadFile ( fileID string , prog chan <- models. Prog ) {
48+ defer close ( prog )
4849 ctx , err := middleware .AddAuthData ()
4950 if err != nil {
50- return nil , err
51+ prog <- models.Prog {Err : err }
52+ return
5153 }
5254 outDir := helpers .DownloadFilesDir
5355 conn , err := grpc .NewClient (helpers .Addr , grpc .WithTransportCredentials (insecure .NewCredentials ()))
5456 if err != nil {
57+ prog <- models.Prog {Err : err }
5558 log .Fatalf ("dial %s: %v" , helpers .Addr , err )
5659 }
5760 defer conn .Close ()
@@ -61,17 +64,20 @@ func DownloadFile(fileID string) (*filev1.GetFilesResponse, error) {
6164
6265 stream , err := c .DownloadFile (ctx , & filev1.DownloadFileRequest {Fileid : fileID })
6366 if err != nil {
64- return nil , err
67+ prog <- models.Prog {Err : err }
68+ return
6569 }
6670
6771 // 1) ждём FileInfo
6872 first , err := stream .Recv ()
6973 if err != nil {
70- return nil , status .Errorf (codes .Internal , "read: %v" , err )
74+ prog <- models.Prog {Err : status .Errorf (codes .Internal , "read: %v" , err )}
75+ return
7176 }
7277 info := first .GetInfo ()
7378 if info == nil {
74- return nil , errors .New ("first message must be FileInfo" )
79+ prog <- models.Prog {Err : errors .New ("first message must be FileInfo" )}
80+ return
7581 }
7682
7783 filename := safeBase (info .GetFilename ())
@@ -81,14 +87,16 @@ func DownloadFile(fileID string) (*filev1.GetFilesResponse, error) {
8187
8288 // 2) готовим пути/директории
8389 if err := os .MkdirAll (outDir , 0o755 ); err != nil {
84- return nil , fmt .Errorf ("mkdir: %w" , err )
90+ prog <- models.Prog {Err : fmt .Errorf ("mkdir: %w" , err )}
91+ return
8592 }
8693 tmpPath := filepath .Join (outDir , filename + ".part" )
8794 finalPath := filepath .Join (outDir , filename )
8895
8996 out , err := os .OpenFile (tmpPath , os .O_CREATE | os .O_WRONLY | os .O_TRUNC , 0o644 )
9097 if err != nil {
91- return nil , fmt .Errorf ("create: %w" , err )
98+ prog <- models.Prog {Err : fmt .Errorf ("create: %w" , err )}
99+ return
92100 }
93101 defer func () {
94102 out .Close ()
@@ -97,12 +105,20 @@ func DownloadFile(fileID string) (*filev1.GetFilesResponse, error) {
97105 }
98106 }()
99107
108+ stat , err := out .Stat ()
109+ if err != nil {
110+ prog <- models.Prog {Err : err }
111+ return
112+ }
113+ total := stat .Size ()
114+
100115 h := sha256 .New ()
101116 var written int64
102117
103118 for {
104119 if ctx .Err () != nil {
105- return nil , ctx .Err ()
120+ prog <- models.Prog {Err : ctx .Err ()}
121+ return
106122 }
107123 msg , rerr := stream .Recv ()
108124 if rerr == io .EOF {
@@ -111,39 +127,49 @@ func DownloadFile(fileID string) (*filev1.GetFilesResponse, error) {
111127 if rerr != nil {
112128 // красиво покажем gRPC статус
113129 if st , ok := status .FromError (rerr ); ok {
114- return nil , fmt .Errorf ("recv: %s" , st .Message ())
130+ prog <- models.Prog {Err : fmt .Errorf ("recv: %s" , st .Message ())}
131+ return
115132 }
116- return nil , fmt .Errorf ("recv: %w" , rerr )
133+ prog <- models.Prog {Err : fmt .Errorf ("recv: %w" , rerr )}
134+ return
117135 }
118136
119137 ch := msg .GetChunk ()
120138 if ch == nil {
121- return nil , errors .New ("unexpected message: want FileChunk" )
139+ prog <- models.Prog {Err : errors .New ("unexpected message: want FileChunk" )}
140+ return
122141 }
123142
124143 n , werr := out .Write (ch .Content )
125144 if werr != nil {
126- return nil , fmt .Errorf ("write: %w" , werr )
145+ prog <- models.Prog {Err : fmt .Errorf ("write: %w" , werr )}
146+ return
127147 }
128148 written += int64 (n )
149+ select {
150+ case prog <- models.Prog {Done : written , Total : total }:
151+ default : // не блокируем UI, если буфер заполнен
152+ }
129153 _ , _ = h .Write (ch .Content )
130154 }
131155
132156 // 4) fsync → close → rename
133157 if err := out .Sync (); err != nil {
134- return nil , fmt .Errorf ("sync: %w" , err )
158+ prog <- models.Prog {Err : fmt .Errorf ("sync: %w" , err )}
159+ return
135160 }
136161 if err := out .Close (); err != nil {
137- return nil , fmt .Errorf ("close: %w" , err )
162+ prog <- models.Prog {Err : fmt .Errorf ("close: %w" , err )}
163+ return
138164 }
139165 if err := os .Rename (tmpPath , finalPath ); err != nil {
140- return nil , fmt .Errorf ("rename: %w" , err )
166+ prog <- models.Prog {Err : fmt .Errorf ("rename: %w" , err )}
167+ return
141168 }
142169
143170 // 5) сверим размер (если сервер прислал size)
144171 if info .Size > 0 && written != info .Size {
145- return nil , fmt .Errorf ("size mismatch: got %d, want %d" , written , info .Size )
172+ prog <- models.Prog {Err : fmt .Errorf ("size mismatch: got %d, want %d" , written , info .Size )}
173+ return
146174 }
147-
148- return nil , nil
149175}
0 commit comments