func downloadFiles(resultItems []AqlSearchResultItem, flags *utils.Flags) {
	size := len(resultItems)
	var wg sync.WaitGroup
	for i := 0; i < flags.Threads; i++ {
		wg.Add(1)
		go func(threadId int) {
			for j := threadId; j < size; j += flags.Threads {
				downloadPath := buildDownloadUrl(flags.ArtDetails.Url, resultItems[j])
				logMsgPrefix := utils.GetLogMsgPrefix(threadId, flags.DryRun)
				fmt.Println(logMsgPrefix + " Downloading " + downloadPath)
				if !flags.DryRun {
					downloadFile(downloadPath, resultItems[j].Path, resultItems[j].Name, logMsgPrefix, flags)
				}
			}
			wg.Done()
		}(i)
	}
	wg.Wait()
}
// Uploads the artifacts in the specified local path pattern to the specified target path.
// Returns the total number of artifacts successfully uploaded.
func Upload(localPath, targetPath string, flags *utils.Flags) (totalUploaded, totalFailed int) {
	// Get the list of artifacts to be uploaded to Artifactory:
	artifacts := getFilesToUpload(localPath, targetPath, flags)
	size := len(artifacts)

	// Start a thread for each channel and start uploading:
	var wg sync.WaitGroup

	// Create an array of integers, to store the total file that were uploaded successfully.
	// Each array item is used by a single thread.
	uploadCount := make([]int, flags.Threads, flags.Threads)

	for i := 0; i < flags.Threads; i++ {
		wg.Add(1)
		go func(threadId int) {
			for j := threadId; j < size; j += flags.Threads {
				target := flags.ArtDetails.Url + artifacts[j].TargetPath
				if uploadFile(artifacts[j].LocalPath, target, flags, utils.GetLogMsgPrefix(threadId, flags.DryRun)) {
					uploadCount[threadId]++
				}
			}
			wg.Done()
		}(i)
	}
	wg.Wait()
	totalUploaded = 0
	for _, i := range uploadCount {
		totalUploaded += i
	}

	fmt.Println("Uploaded " + strconv.Itoa(totalUploaded) + " artifacts to Artifactory.")
	totalFailed = size - totalUploaded
	if totalFailed > 0 {
		fmt.Println("Failed uploading " + strconv.Itoa(totalFailed) + " artifacts to Artifactory.")
	}
	return
}