@@ -10,7 +10,7 @@ import (
10
10
"os"
11
11
"path"
12
12
"strconv"
13
- "sync "
13
+ "strings "
14
14
"time"
15
15
16
16
"github.com/linuxsuren/http-downloader/pkg/common"
@@ -50,6 +50,7 @@ type HTTPDownloader struct {
50
50
Debug bool
51
51
RoundTripper http.RoundTripper
52
52
progressIndicator * ProgressIndicator
53
+ suggestedFilename string
53
54
}
54
55
55
56
// SetProxy set the proxy for a http
@@ -150,6 +151,14 @@ func (h *HTTPDownloader) DownloadFile() error {
150
151
}
151
152
}
152
153
154
+ if disposition , ok := resp .Header ["Content-Disposition" ]; ok && len (disposition ) >= 1 {
155
+ h .suggestedFilename = strings .TrimPrefix (disposition [0 ], `filename="` )
156
+ h .suggestedFilename = strings .TrimSuffix (h .suggestedFilename , `"` )
157
+ if h .suggestedFilename == filepath {
158
+ h .suggestedFilename = ""
159
+ }
160
+ }
161
+
153
162
// pre-hook before get started to download file
154
163
if h .PreStart != nil && ! h .PreStart (resp ) {
155
164
return nil
@@ -192,127 +201,15 @@ func (h *HTTPDownloader) DownloadFile() error {
192
201
return err
193
202
}
194
203
195
- // DownloadFileWithMultipleThread downloads the files with multiple threads
196
- func DownloadFileWithMultipleThread (targetURL , targetFilePath string , thread int , showProgress bool ) (err error ) {
197
- return DownloadFileWithMultipleThreadKeepParts (targetURL , targetFilePath , thread , false , showProgress )
204
+ // GetSuggestedFilename returns the suggested filename which comes from the HTTP response header.
205
+ // Returns empty string if the filename is same with the given name.
206
+ func (h * HTTPDownloader ) GetSuggestedFilename () string {
207
+ return h .suggestedFilename
198
208
}
199
209
200
- // MultiThreadDownloader is a download with multi-thread
201
- type MultiThreadDownloader struct {
202
- noProxy bool
203
- keepParts , showProgress bool
204
-
205
- roundTripper http.RoundTripper
206
- }
207
-
208
- // WithoutProxy indicates not use HTTP proxy
209
- func (d * MultiThreadDownloader ) WithoutProxy (noProxy bool ) * MultiThreadDownloader {
210
- d .noProxy = noProxy
211
- return d
212
- }
213
-
214
- // WithShowProgress indicate if show the download progress
215
- func (d * MultiThreadDownloader ) WithShowProgress (showProgress bool ) * MultiThreadDownloader {
216
- d .showProgress = showProgress
217
- return d
218
- }
219
-
220
- // WithKeepParts indicates if keeping the part files
221
- func (d * MultiThreadDownloader ) WithKeepParts (keepParts bool ) * MultiThreadDownloader {
222
- d .keepParts = keepParts
223
- return d
224
- }
225
-
226
- // WithRoundTripper sets RoundTripper
227
- func (d * MultiThreadDownloader ) WithRoundTripper (roundTripper http.RoundTripper ) * MultiThreadDownloader {
228
- d .roundTripper = roundTripper
229
- return d
230
- }
231
-
232
- // Download starts to download the target URL
233
- func (d * MultiThreadDownloader ) Download (targetURL , targetFilePath string , thread int ) (err error ) {
234
- // get the total size of the target file
235
- var total int64
236
- var rangeSupport bool
237
- if total , rangeSupport , err = DetectSizeWithRoundTripper (targetURL , targetFilePath , true , d .noProxy , d .roundTripper ); err != nil {
238
- return
239
- }
240
-
241
- if rangeSupport {
242
- unit := total / int64 (thread )
243
- offset := total - unit * int64 (thread )
244
- var wg sync.WaitGroup
245
- var partItems []string
246
- var m sync.Mutex
247
-
248
- defer func () {
249
- // remove all partial files
250
- for _ , part := range partItems {
251
- _ = os .RemoveAll (part )
252
- }
253
- }()
254
-
255
- fmt .Printf ("start to download with %d threads, size: %d, unit: %d\n " , thread , total , unit )
256
- for i := 0 ; i < thread ; i ++ {
257
- wg .Add (1 )
258
- go func (index int , wg * sync.WaitGroup ) {
259
- defer wg .Done ()
260
- output := fmt .Sprintf ("%s-%d" , targetFilePath , index )
261
-
262
- m .Lock ()
263
- partItems = append (partItems , output )
264
- m .Unlock ()
265
-
266
- end := unit * int64 (index + 1 ) - 1
267
- if index == thread - 1 {
268
- // this is the last part
269
- end += offset
270
- }
271
- start := unit * int64 (index )
272
-
273
- downloader := & ContinueDownloader {}
274
- downloader .WithoutProxy (d .noProxy ).
275
- WithRoundTripper (d .roundTripper )
276
- if downloadErr := downloader .DownloadWithContinue (targetURL , output ,
277
- int64 (index ), start , end , d .showProgress ); downloadErr != nil {
278
- fmt .Println (downloadErr )
279
- }
280
- }(i , & wg )
281
- }
282
-
283
- wg .Wait ()
284
- ProgressIndicator {}.Close ()
285
-
286
- // concat all these partial files
287
- var f * os.File
288
- if f , err = os .OpenFile (targetFilePath , os .O_CREATE | os .O_WRONLY , 0600 ); err == nil {
289
- defer func () {
290
- _ = f .Close ()
291
- }()
292
-
293
- for i := 0 ; i < thread ; i ++ {
294
- partFile := fmt .Sprintf ("%s-%d" , targetFilePath , i )
295
- if data , ferr := os .ReadFile (partFile ); ferr == nil {
296
- if _ , err = f .Write (data ); err != nil {
297
- err = fmt .Errorf ("failed to write file: '%s'" , partFile )
298
- break
299
- } else if ! d .keepParts {
300
- _ = os .RemoveAll (partFile )
301
- }
302
- } else {
303
- err = fmt .Errorf ("failed to read file: '%s'" , partFile )
304
- break
305
- }
306
- }
307
- }
308
- } else {
309
- fmt .Println ("cannot download it using multiple threads, failed to one" )
310
- downloader := & ContinueDownloader {}
311
- downloader .WithoutProxy (d .noProxy )
312
- downloader .WithRoundTripper (d .roundTripper )
313
- err = downloader .DownloadWithContinue (targetURL , targetFilePath , - 1 , 0 , 0 , true )
314
- }
315
- return
210
+ // SuggestedFilenameAware is the interface for getting suggested filename
211
+ type SuggestedFilenameAware interface {
212
+ GetSuggestedFilename () string
316
213
}
317
214
318
215
// DownloadFileWithMultipleThreadKeepParts downloads the files with multiple threads
@@ -326,8 +223,14 @@ func DownloadFileWithMultipleThreadKeepParts(targetURL, targetFilePath string, t
326
223
type ContinueDownloader struct {
327
224
downloader * HTTPDownloader
328
225
329
- roundTripper http.RoundTripper
330
- noProxy bool
226
+ roundTripper http.RoundTripper
227
+ noProxy bool
228
+ insecureSkipVerify bool
229
+ }
230
+
231
+ // GetSuggestedFilename returns the suggested filename
232
+ func (c * ContinueDownloader ) GetSuggestedFilename () string {
233
+ return c .downloader .GetSuggestedFilename ()
331
234
}
332
235
333
236
// WithRoundTripper set WithRoundTripper
@@ -342,14 +245,21 @@ func (c *ContinueDownloader) WithoutProxy(noProxy bool) *ContinueDownloader {
342
245
return c
343
246
}
344
247
248
+ // WithInsecureSkipVerify set if skip the insecure verify
249
+ func (c * ContinueDownloader ) WithInsecureSkipVerify (insecureSkipVerify bool ) * ContinueDownloader {
250
+ c .insecureSkipVerify = insecureSkipVerify
251
+ return c
252
+ }
253
+
345
254
// DownloadWithContinue downloads the files continuously
346
255
func (c * ContinueDownloader ) DownloadWithContinue (targetURL , output string , index , continueAt , end int64 , showProgress bool ) (err error ) {
347
256
c .downloader = & HTTPDownloader {
348
- TargetFilePath : output ,
349
- URL : targetURL ,
350
- ShowProgress : showProgress ,
351
- NoProxy : c .noProxy ,
352
- RoundTripper : c .roundTripper ,
257
+ TargetFilePath : output ,
258
+ URL : targetURL ,
259
+ ShowProgress : showProgress ,
260
+ NoProxy : c .noProxy ,
261
+ RoundTripper : c .roundTripper ,
262
+ InsecureSkipVerify : c .insecureSkipVerify ,
353
263
}
354
264
if index >= 0 {
355
265
c .downloader .Title = fmt .Sprintf ("Downloading part %d" , index )
@@ -371,21 +281,16 @@ func (c *ContinueDownloader) DownloadWithContinue(targetURL, output string, inde
371
281
return
372
282
}
373
283
374
- // DetectSize returns the size of target resource
375
- //
376
- // Deprecated, use DetectSizeWithRoundTripper instead
377
- func DetectSize (targetURL , output string , showProgress bool ) (int64 , bool , error ) {
378
- return DetectSizeWithRoundTripper (targetURL , output , showProgress , false , nil )
379
- }
380
-
381
284
// DetectSizeWithRoundTripper returns the size of target resource
382
- func DetectSizeWithRoundTripper (targetURL , output string , showProgress bool , noProxy bool , roundTripper http.RoundTripper ) (total int64 , rangeSupport bool , err error ) {
285
+ func DetectSizeWithRoundTripper (targetURL , output string , showProgress , noProxy , insecureSkipVerify bool ,
286
+ roundTripper http.RoundTripper ) (total int64 , rangeSupport bool , err error ) {
383
287
downloader := HTTPDownloader {
384
- TargetFilePath : output ,
385
- URL : targetURL ,
386
- ShowProgress : showProgress ,
387
- RoundTripper : roundTripper ,
388
- NoProxy : false , // below HTTP request does not need proxy
288
+ TargetFilePath : output ,
289
+ URL : targetURL ,
290
+ ShowProgress : showProgress ,
291
+ RoundTripper : roundTripper ,
292
+ NoProxy : false , // below HTTP request does not need proxy
293
+ InsecureSkipVerify : insecureSkipVerify ,
389
294
}
390
295
391
296
var detectOffset int64
@@ -400,6 +305,8 @@ func DetectSizeWithRoundTripper(targetURL, output string, showProgress bool, noP
400
305
contentLen := resp .Header .Get ("Content-Length" )
401
306
if total , lenErr = strconv .ParseInt (contentLen , 10 , 0 ); lenErr == nil {
402
307
total += detectOffset
308
+ } else {
309
+ rangeSupport = false
403
310
}
404
311
// always return false because we just want to get the header from response
405
312
return false
0 commit comments