@@ -75,8 +75,32 @@ func DownloadIndex(indexPath pathutils.Path, URL string) error {
75
75
return nil
76
76
}
77
77
78
- // DownloadPackage downloads a package from arduino repository.
79
- func DownloadPackage (URL string , initialData * os.File , totalSize int64 , handleResultFunc func (io.Reader , * os.File , int ) error ) error {
78
+ // HandleResultFunc defines a function able to handle the content of the
79
+ // download stream of the package (DownloadPackage), filling the File with the content
80
+ // of the Reader, starting from the initial position
81
+ type HandleDownloadPackageResultFunc func (io.Reader , * os.File , int ) error
82
+
83
+ // DefaultDownloadHandlerFunc is the default HandleDownloadPackageResultFunc, which
84
+ // simply copies the content of the Reader into the File, starting from the initialSize
85
+ func DefaultDownloadHandlerFunc (source io.Reader , initialData * os.File , initialSize int ) error {
86
+ // Copy the file content
87
+ _ , err := io .Copy (initialData , source )
88
+ return err
89
+ }
90
+
91
+ // DownloadPackageProgressChangedHandler defines a function able to handle the update
92
+ // of the progress of the current download
93
+ type DownloadPackageProgressChangedHandler func (fileSize int64 , downloadedSoFar int64 )
94
+
95
+ // DownloadPackage downloads a package from Arduino repository.
96
+ // Besides the download information (URL, initialData and totalSize), two external handlers are available for:
97
+ // - (handleResultFunc) handling the result of the download (i.e. decide how to copy the download to the file
98
+ // or do something weird during the operation)
99
+ // - (progressChangedHandler) handling the download progress change (and perhaps display it somehow)
100
+ // None of the handlers is mandatory; they won't be used if nil.
101
+ func DownloadPackage (URL string , initialData * os.File , totalSize int64 , handleResultFunc HandleDownloadPackageResultFunc ,
102
+ progressChangedHandler DownloadPackageProgressChangedHandler ) error {
103
+
80
104
if initialData == nil {
81
105
return errors .New ("Cannot fill a nil file pointer" )
82
106
}
@@ -120,13 +144,51 @@ func DownloadPackage(URL string, initialData *os.File, totalSize int64, handleRe
120
144
}
121
145
defer response .Body .Close ()
122
146
147
+ // Handle the progress update handler, by creating a ProgressProxyReader;
148
+ // only if it's needed (i.e. we actually have an external progressChangedHandler)
149
+ progressProxyReader := response .Body
150
+ downloadedSoFar := initialSize
151
+ if progressChangedHandler != nil {
152
+ progressProxyReader = ProgressProxyReader {response .Body , func (progressDelta int64 ) {
153
+ // WARNING: This is using a closure on downloadedSoFar!
154
+ downloadedSoFar += progressDelta
155
+ progressChangedHandler (totalSize , downloadedSoFar )
156
+ },
157
+ }
158
+ }
159
+
160
+ // Use the external handleResultFunc, if available, or the default one otherwise
123
161
if handleResultFunc == nil {
124
- _ , err = io .Copy (initialData , response .Body )
125
- } else {
126
- err = handleResultFunc (response .Body , initialData , int (initialSize ))
162
+ handleResultFunc = DefaultDownloadHandlerFunc
127
163
}
164
+
165
+ err = handleResultFunc (progressProxyReader , initialData , int (initialSize ))
128
166
if err != nil {
129
167
return fmt .Errorf ("Cannot read response body from %s : %s" , URL , err )
130
168
}
131
169
return nil
132
170
}
171
+
172
+ // FIXME: Move outside? perhaps in commons?
173
+ // HandleProgressUpdateFunc defines a function able to handle the progressDelta, in bytes
174
+ type HandleProgressUpdateFunc func (progressDelta int64 )
175
+
176
+ // It's proxy reader, intercepting reads to post progress updates, implement io.Reader
177
+ type ProgressProxyReader struct {
178
+ io.Reader
179
+ handleProgressUpdateFunc HandleProgressUpdateFunc
180
+ }
181
+
182
+ func (r ProgressProxyReader ) Read (p []byte ) (n int , err error ) {
183
+ n , err = r .Reader .Read (p )
184
+ r .handleProgressUpdateFunc (int64 (n ))
185
+ return
186
+ }
187
+
188
+ // Close the reader when it implements io.Closer
189
+ func (r ProgressProxyReader ) Close () (err error ) {
190
+ if closer , ok := r .Reader .(io.Closer ); ok {
191
+ return closer .Close ()
192
+ }
193
+ return
194
+ }
0 commit comments