/// <summary> /// Walks the byte ranges of the image file to construct the logical parts we'll use to perform /// the upload /// </summary> /// <param name="volumeSize">The requested size of the volume in EC2</param> /// <param name="urlExpiration">The time at which the presigned urls for the parts should expire.</param> /// <returns>Manifest subcomponent describing the part structure.</returns> Import ConstructImportPartsList(long? volumeSize, DateTime urlExpiration) { var imageFilename = Path.GetFileName(ImageFilePath); var imageFileinfo = new FileInfo(ImageFilePath); var diskImageSize = imageFileinfo.Length; var partCount = (int)Math.Ceiling((double)diskImageSize / DefaultPartSize); var parts = new ImageFileParts { Count = partCount }; var partKeyPrefix = string.Format(CultureInfo.InvariantCulture, "{0}/{1}.", ArtifactsKeyPrefix, imageFilename); long partStartOffset = 0; for (var i = 0; i < partCount; i++) { var partEndOffset = partStartOffset + DefaultPartSize - 1; if (partEndOffset >= diskImageSize) partEndOffset = diskImageSize - 1; var partKey = string.Concat(partKeyPrefix, PartSuffix, i); var part = new ImageFilePart { Index = i, ByteRange = new ImageFilePartByteRange { Start = partStartOffset, End = partEndOffset }, Key = partKey, HeadUrl = S3Client.GetPreSignedURL(new GetPreSignedUrlRequest { BucketName = BucketName, Key = partKey, Verb = HttpVerb.HEAD, Expires = urlExpiration }), GetUrl = S3Client.GetPreSignedURL(new GetPreSignedUrlRequest { BucketName = BucketName, Key = partKey, Verb = HttpVerb.GET, Expires = urlExpiration }), DeleteUrl = S3Client.GetPreSignedURL(new GetPreSignedUrlRequest { BucketName = BucketName, Key = partKey, Verb = HttpVerb.DELETE, Expires = urlExpiration }) }; parts.PartInstances.Add(part); partStartOffset += DefaultPartSize; } return new Import { Size = diskImageSize, VolumeSize = VolumeSizeFor(diskImageSize, volumeSize), PartsList = parts }; }