示例#1
0
        private async Task <IEnumerable <ChunkInfo> > UploadChunksAsync(
            WebApi webApi, IFileStorage fileStorage, List <ChunkInfo> chunks, int progressBaseline, CancellationToken cancellationToken, int parallelism)
        {
            // Assume all chunks failed until successfully uploaded
            var  failedChunks = new List <ChunkInfo>(chunks.ToArray());
            long totalBytes   = chunks.Sum(chunk => chunk.TotalBytes);

            if (totalBytes > MinimumByteCount)
            {
                this.EstimatedTimeRemaining = "Estimating remaining time";
            }

            Stopwatch stopwatch = Stopwatch.StartNew();

            // Create a callback that updates the progress and remaining time estimate
            Action updateProgress = () =>
            {
                long totalBytesSent = chunks.Sum(c => c.BytesSent);
                if (totalBytesSent >= MinimumByteCount)
                {
                    long     totalBytesRemaining = totalBytes - totalBytesSent;
                    TimeSpan remainingTime       = TimeSpan.FromSeconds(
                        Math.Max(1, Math.Round((double)totalBytesRemaining * stopwatch.Elapsed.TotalSeconds / (double)totalBytesSent)));
                    double mbps = (double)totalBytesSent / stopwatch.Elapsed.TotalSeconds * 8e-6;
                    this.EstimatedTimeRemaining = string.Format(
                        CultureInfo.CurrentCulture, "About {0} remaining ({1:#,##0.##} Mbps)", remainingTime.FormatTimeSpan(), mbps);
                }

                double progressFraction = totalBytesSent / (double)totalBytes;
                this.ProgressPercentage = progressBaseline + 90 * progressFraction;
            };

            this.Status = "Uploading files...";
            Action <IEnumerable <ChunkInfo> > uploadChunks = chunkSlice =>
            {
                foreach (var chunk in chunkSlice)
                {
                    using (var stream = fileStorage.GetFileStream(chunk.FilePath, FileMode.Open))
                    {
                        bool uploaded = webApi.UploadFileAsync(chunk.UploadUri, stream, cancellationToken, bytesSent =>
                        {
                            chunk.BytesSent = bytesSent;
                            updateProgress();
                        }).Result;
                        if (uploaded)
                        {
                            chunk.BytesSent = chunk.TotalBytes;
                            lock (failedChunks)
                            {
                                failedChunks.Remove(chunk);
                            }
                        }

                        updateProgress();
                    }

                    if (cancellationToken.IsCancellationRequested)
                    {
                        break;
                    }
                }
            };

            // Split into n segments and assign a task to each
            int sliceCount  = Math.Min(chunks.Count, parallelism);
            int sliceLength = chunks.Count / sliceCount;
            await Task.WhenAll(Enumerable.Range(0, sliceCount).Select(i =>
                                                                      Task.Run(() =>
            {
                if (i == sliceCount - 1)
                {
                    // The last slice may be larger than the rest
                    uploadChunks(chunks.Skip(i *sliceLength));
                }
                else
                {
                    uploadChunks(chunks.Skip(i *sliceLength).Take(sliceLength));
                }
            })).ToArray());

            // Measure the upload rate in megabits per second. (There are 8 bits per byte and 10^6 bits per megabit, so we scale bytes-per-second by 8*10^-6.)
            stopwatch.Stop();
            double averageMbps = (double)totalBytes / stopwatch.Elapsed.TotalSeconds * 8e-6;

            this.EstimatedTimeRemaining = string.Format("Average upload speed: {0:#,##0.##} Mbps", averageMbps);

            return(failedChunks);
        }
示例#2
0
        /// <summary>
        /// Asynchronously uploads a synth packet to the Photosynth upload service for processing in the cloud.
        /// </summary>
        /// <param name="webApi">A WebApi object for handling HTTP communication with the Photosynth web service</param>
        /// <param name="fileStorage">An IFileStorage implementation for handling file I/O</param>
        /// <param name="synthPacketUpload">Metadata for the synth packet to be uploaded.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <param name="parallelism">The maximum number of chunks that can be uploaded in parallel.</param>
        /// <returns>The task object representing the asynchronous operation</returns>
        public async Task UploadSynthPacketAsync(
            WebApi webApi, IFileStorage fileStorage, SynthPacketUpload synthPacketUpload, CancellationToken cancellationToken, int parallelism = 2)
        {
            if (this.IsUploading)
            {
                throw new InvalidOperationException("Only one upload at a time per Uploader instance is allowed.");
            }

            if (string.IsNullOrWhiteSpace(synthPacketUpload.Title))
            {
                throw new ArgumentException("Must provide a Title for the SynthPacketUpload.");
            }

            try
            {
                this.IsUploading            = true;
                this.Id                     = Guid.Empty;
                this.ProgressPercentage     = 0;
                this.EstimatedTimeRemaining = string.Empty;
                this.FailedChunks.Clear();

                // Create a new Photosynth collection
                this.Status = "Creating collection...";
                var createCollectionRequest = new CreateMediaRequest()
                {
                    UploadType = UploadType.SynthPacketFromRawImages
                };
                var createCollectionResponse = await webApi.CreateMediaAsync(createCollectionRequest);

                this.ProgressPercentage = 1;

                this.Id = createCollectionResponse.Id;

                // Add images to the collection and get upload URLs for each file
                this.Status = "Adding images to collection...";
                var addFilesRequest = new AddFilesRequest();
                for (int i = 0; i < synthPacketUpload.PhotoPaths.Count; i++)
                {
                    addFilesRequest.Files.Add(new AddFileRequest()
                    {
                        Id         = i.ToString(),
                        Extension  = "jpg",
                        Order      = i.ToString("000"),
                        ChunkCount = 1,
                    });
                }

                var addFilesResponse = await webApi.AddFilesAsync(this.Id, addFilesRequest);

                this.ProgressPercentage = 2;

                // Get info about all the files that need uploading
                List <ChunkInfo> chunks = addFilesRequest.Files.Join(
                    addFilesResponse.Files,
                    req => req.Id,
                    resp => resp.ClientId,
                    (req, resp) =>
                {
                    string photoPath = synthPacketUpload.PhotoPaths[Int32.Parse(resp.ClientId)];
                    FileChunk chunk  = resp.Chunks.First();
                    return(new ChunkInfo(chunk.UploadUri, photoPath, fileStorage.GetFileSize(photoPath)));
                }).ToList();

                // Commit the collection, no more files can be added
                this.Status = "Setting properties for the collection...";
                var editMediaRequest = new EditMediaRequest()
                {
                    Name         = synthPacketUpload.Title,
                    Description  = synthPacketUpload.Description,
                    ImageCount   = synthPacketUpload.PhotoPaths.Count,
                    PrivacyLevel = synthPacketUpload.PrivacyLevel,
                    CapturedDate = synthPacketUpload.CapturedDate,
                    Committed    = true,
                    SynthPacket  = new SynthPacket()
                    {
                        License = synthPacketUpload.License,
                    },
                    UploadHints = synthPacketUpload.Topology.ToString()
                                  // TODO geotag
                };
                if (synthPacketUpload.Tags.Any())
                {
                    editMediaRequest.Tags = string.Join(",", synthPacketUpload.Tags);
                }

                await webApi.EditMediaAsync(this.Id, editMediaRequest);

                this.ProgressPercentage = 3;

                var failedChunks = await this.UploadChunksAsync(webApi, fileStorage, chunks, 3, cancellationToken, parallelism);

                foreach (var failedChunk in failedChunks)
                {
                    this.FailedChunks.Add(failedChunk);
                }

                this.ProgressPercentage = 100;
                if (this.FailedChunks.Any())
                {
                    this.Status = string.Format(CultureInfo.CurrentUICulture, "{0} files failed to upload.", this.FailedChunks.Count);
                }
                else
                {
                    this.Status = string.Format(CultureInfo.CurrentUICulture, "{0} files successfully uploaded.", chunks.Count);
                }
            }
            catch (Exception e)
            {
                this.Status = string.Format(CultureInfo.CurrentUICulture, "Error: {0}", e.Message);
            }
            finally
            {
                this.IsUploading = false;
            }
        }