code.cloudfoundry.org/cli@v7.1.0+incompatible/cf/util/downloader/file_download.go (about)

     1  package downloader
     2  
     3  import (
     4  	"code.cloudfoundry.org/cli/util"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  )
    12  
    13  type Downloader interface {
    14  	DownloadFile(string) (int64, string, error)
    15  	RemoveFile() error
    16  	SavePath() string
    17  }
    18  
    19  type downloader struct {
    20  	saveDir    string
    21  	filename   string
    22  	downloaded bool
    23  }
    24  
    25  func NewDownloader(saveDir string) Downloader {
    26  	return &downloader{
    27  		saveDir:    saveDir,
    28  		downloaded: false,
    29  	}
    30  }
    31  
    32  //this func returns byte written, filename and error
    33  func (d *downloader) DownloadFile(url string) (int64, string, error) {
    34  	c := http.Client{
    35  		Transport: &http.Transport{
    36  			TLSClientConfig: util.NewTLSConfig(nil, false),
    37  		},
    38  		CheckRedirect: func(r *http.Request, via []*http.Request) error {
    39  			r.URL.Opaque = r.URL.Path
    40  
    41  			//some redirect return '/' as url
    42  			if strings.Trim(r.URL.Opaque, "/") != "" {
    43  				url = r.URL.Opaque
    44  			}
    45  
    46  			return nil
    47  		},
    48  	}
    49  
    50  	r, err := c.Get(url)
    51  
    52  	if err != nil {
    53  		return 0, "", err
    54  	}
    55  	defer r.Body.Close()
    56  
    57  	if r.StatusCode == 200 {
    58  		d.filename = getFilenameFromHeader(r.Header.Get("Content-Disposition"))
    59  
    60  		if d.filename == "" {
    61  			d.filename = getFilenameFromURL(url)
    62  		}
    63  
    64  		f, err := os.Create(filepath.Join(d.saveDir, d.filename))
    65  		if err != nil {
    66  			return 0, "", err
    67  		}
    68  		defer f.Close()
    69  
    70  		size, err := io.Copy(f, r.Body)
    71  		if err != nil {
    72  			return 0, "", err
    73  		}
    74  
    75  		d.downloaded = true
    76  		return size, d.filename, nil
    77  
    78  	}
    79  	return 0, "", fmt.Errorf("Error downloading file from %s", url)
    80  }
    81  
    82  func (d *downloader) RemoveFile() error {
    83  	if !d.downloaded {
    84  		return nil
    85  	}
    86  	d.downloaded = false
    87  	return os.Remove(filepath.Join(d.saveDir, d.filename))
    88  }
    89  
    90  func getFilenameFromHeader(h string) string {
    91  	if h == "" {
    92  		return ""
    93  	}
    94  
    95  	contents := strings.Split(h, ";")
    96  	for _, content := range contents {
    97  		if strings.Contains(content, "filename=") {
    98  			content = strings.TrimSpace(content)
    99  			name := strings.TrimLeft(content, "filename=")
   100  			return strings.Trim(name, `"`)
   101  		}
   102  	}
   103  
   104  	return ""
   105  }
   106  
   107  func getFilenameFromURL(url string) string {
   108  	tmp := strings.Split(url, "/")
   109  	token := tmp[len(tmp)-1]
   110  
   111  	if i := strings.LastIndex(token, "?"); i != -1 {
   112  		token = token[i+1:]
   113  	}
   114  
   115  	if i := strings.LastIndex(token, "&"); i != -1 {
   116  		token = token[i+1:]
   117  	}
   118  
   119  	if i := strings.LastIndex(token, "="); i != -1 {
   120  		return token[i+1:]
   121  	}
   122  
   123  	return token
   124  }
   125  
   126  func (d *downloader) SavePath() string {
   127  	return d.saveDir
   128  }