示例#1
0
// 1. Run "go get github.com/northbright/pathhelper" to install pathhelper.
// 2. Run "go test -c && ./ctxcopy.test"
func ExampleCopy() {
	// Download a zip from web server to local storage to test Copy().
	url := "https://github.com/northbright/plants/archive/master.zip"
	totalTimeoutSeconds := 10 // to make download successful, set it to 300 or more.
	totalTimeout := time.Duration(time.Duration(totalTimeoutSeconds) * time.Second)

	// Make context to carry a deadline(timeout).
	// See http://blog.golang.org/context for more information.
	ctx, cancel := context.WithTimeout(context.Background(), totalTimeout)
	defer cancel()

	// Get response body for source.
	resp, err := http.Get(url)
	if err != nil {
		fmt.Fprintf(os.Stderr, "http.Get(%v) err: %v\n", url, err)
		return
	}
	defer resp.Body.Close()

	// Create a file for destination.
	fileName, _ := pathhelper.GetAbsPath("./1.zip")
	f, err := os.Create(fileName)
	if err != nil {
		fmt.Fprintf(os.Stderr, "os.Create(%v) err: %v\n", fileName, err)
		return
	}
	defer f.Sync()
	defer f.Close()

	buf := make([]byte, 2*1024*1024)

	// Copy starts.
	// Copy operation will be canceled if cancel() is called in other goroutine.
	// Copy operation will be stoped if deadline is exceeded(timeout).
	err = ctxcopy.Copy(ctx, f, resp.Body, buf)
	if err != nil {
		fmt.Fprintf(os.Stderr, "ctxcopy.Copy() err: %v\n", err)
	} else {
		fmt.Fprintf(os.Stderr, "ctxcopy.Copy() succeeded.\n")
	}

	// Output:
}
示例#2
0
// Download downloads the file from HTTP server.
//
//   Params:
//     ctx:
//       Google's Context type which carries deadlines, cacelation signals,
//       and other request-scoped values across API boundaries and between processes.
//       See https://godoc.org/golang.org/x/net/context for more.
//     url:
//       Download URL
//     outDir:
//       Directory to store the downloaded file.
//     fileName:
//       Downloaded file name(base name). If the given file name is empty(""), it'll detect the file name in response header.
//     buf:
//       Buffer(length should >= 0).
//     requestTimeoutSeconds:
//       HTTP request timeout. It's NOT download timeout. Default value(defRequestTimeoutSeconds) is 10 seconds.
//
//   Return:
//     downloadedFileName: Absolute downloaded file path.
func Download(ctx context.Context, url, outDir, fileName string, buf []byte, requestTimeoutSeconds int) (downloadedFileName string, err error) {
	// Get absolute path of out dir.
	// It'll join the directory of current executable and input path if it's relative.
	absOutDir := ""
	if absOutDir, err = pathhelper.GetAbsPath(outDir); err != nil {
		return "", err
	}

	// Make out dir
	if err = os.MkdirAll(absOutDir, 0755); err != nil {
		return "", err
	}

	var (
		newCtx context.Context
		cancel context.CancelFunc
	)

	// Check request timeout
	if requestTimeoutSeconds <= 0 {
		requestTimeoutSeconds = defRequestTimeoutSeconds
	}

	// Derive new context with request timeout.
	reqTimeout := time.Duration(time.Duration(requestTimeoutSeconds) * time.Second)
	if ctx != nil {
		newCtx, cancel = context.WithTimeout(ctx, reqTimeout)
	} else {
		newCtx, cancel = context.WithTimeout(context.Background(), reqTimeout)
	}
	defer cancel()

	// Do HTTP Request by using golang.org/x/net/context/ctxhttp
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return "", err
	}

	// Accept Encoding:gzip
	req.Header.Set("Accept-Encoding", "gzip")
	resp, err := ctxhttp.Do(newCtx, nil, req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	// Read from response.Body or gzip reader
	var reader io.ReadCloser
	switch resp.Header.Get("Content-Encoding") {
	case "gzip":
		reader, _ = gzip.NewReader(resp.Body)
		defer reader.Close()
	default:
		reader = resp.Body
	}

	// Try to get file name in response header
	if fileName == "" {
		if fileName, err = httputil.GetFileName(url); err != nil {
			return "", err
		}
	}

	// Get final absolute file name.
	absFileName := path.Join(absOutDir, fileName)

	// Create a new file.
	f, err := os.Create(absFileName)
	if err != nil {
		return "", err
	}

	// Call ctxcopy.Copy() to perform Context-aware copy task.
	err = ctxcopy.Copy(ctx, f, reader, buf)
	f.Close()

	if err != nil {
		os.Remove(absFileName)
		return "", err
	}

	return absFileName, nil
}