/// <summary> /// Uploads a file from the local workstation to S3. /// </summary> /// <param name="sourcePath">The source file path.</param> /// <param name="targetUri"> /// The target S3 URI. This may be either an <b>s3://BUCKET/KEY</b> or a /// <b>https://s3.REGION.amazonaws.com/BUCKET/KEY</b> URI referencing an S3 /// bucket and key. /// </param> /// <param name="gzip">Optionally indicates that the target content encoding should be set to <b>gzip</b>.</param> /// <param name="metadata"> /// <para> /// Optionally specifies HTTP metadata headers to be returned when the object /// is downloaded from S3. This formatted as as comma separated a list of /// key/value pairs like: /// </para> /// <example> /// Content-Type=text,app-version=1.0.0 /// </example> /// <note> /// <para> /// AWS supports <b>system</b> as well as <b>custom</b> headers. System headers /// include standard HTTP headers such as <b>Content-Type</b> and <b>Content-Encoding</b>. /// Custom headers are required to include the <b>x-amz-meta-</b> prefix. /// </para> /// <para> /// You don't need to specify the <b>x-amz-meta-</b> prefix for setting custom /// headers; the AWS-CLI detects custom header names and adds the prefix automatically. /// This method will strip the prefix if present before calling the AWS-CLI to ensure /// the prefix doesn't end up being duplicated. /// </para> /// </note> /// </param> /// <param name="publicReadAccess">Optionally grant the upload public read access.</param> public static void S3Upload(string sourcePath, string targetUri, bool gzip = false, string metadata = null, bool publicReadAccess = false) { Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(sourcePath), nameof(sourcePath)); Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(targetUri), nameof(targetUri)); // $todo(jefflill): // // Hardcoding [max_concurrent_requests = 5] for now, down from the default value: 10. // I believe the higher setting is making uploads less reliable and may also consume // too much bandwidth. We should probably make this a parameter. // // Note this changes this setting system side. ExecuteSafe("configure", "set", "default.s3.max_concurrent_requests", "5"); // Perform the upload. var s3Uri = NetHelper.ToAwsS3Uri(targetUri); var args = new List <string>() { "s3", "cp", sourcePath, s3Uri }; if (gzip) { args.Add("--content-encoding"); args.Add("gzip"); } var sbMetadata = new StringBuilder(); if (!string.IsNullOrEmpty(metadata) && metadata.Contains('=')) { foreach (var item in metadata.Split(',', StringSplitOptions.RemoveEmptyEntries)) { // Strip off the [x-amz-meta-] prefix from the name, if present. // Otherwise, the AWS-CLI will add the prefix again, duplicating it. const string customPrefix = "x-amz-meta-"; var fields = item.Split('=', 2, StringSplitOptions.RemoveEmptyEntries); if (fields.Length != 2) { throw new ArgumentException($"Invalid metadata [{metadata}].", nameof(metadata)); } var name = fields[0].Trim(); var value = fields[1].Trim(); if (value == string.Empty) { // Ignore metadata with empty values. continue; } if (name.StartsWith(customPrefix)) { name = name.Substring(customPrefix.Length); metadata = $"{name}={value}"; } // Some headers are considered to be "system defined" and need to be // passed as command line options. We'll separate those out, add the // necessary options to the arguments and add any remaining headers // to the metadata builder. switch (name.ToLowerInvariant()) { case "content-type": case "cache-control": case "content-disposition": case "content-encoding": case "content-language": case "expires": args.Add("--" + name.ToLowerInvariant()); args.Add(value); break; default: sbMetadata.AppendWithSeparator($"{name}={value}", ","); break; } } } if (sbMetadata.Length > 0) { args.Add("--metadata"); args.Add(sbMetadata.ToString()); } AddDebugOption(args); s3Retry.Invoke(() => ExecuteSafe(args.ToArray())); if (publicReadAccess) { var uri = new Uri(s3Uri, UriKind.Absolute); var bucket = uri.Host; var key = uri.AbsolutePath.Substring(1); s3Retry.Invoke(() => ExecuteSafe("s3api", "put-object-acl", "--bucket", bucket, "--key", key, "--acl", "public-read")); } }
public void QueryAsyncSku(ReadOnlyCollection <ProductDefinition> products, Action <List <AndroidJavaObject> > onSkuDetailsResponse) { m_RetryPolicy.Invoke(retryAction => QueryAsyncSkuWithRetries(products, onSkuDetailsResponse, retryAction)); }