using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;
using System.Net.Http.Headers;
using System.Globalization;
using System.Threading;
using System.Text;
namespace UploadSample
{
public class MultiPartUploadProgram
{
public enum FileType
{
MDF = 0,
BAK = 1,
DACPAC = 2
}
public static async Task Main(string[] args)
{
using (HttpClient client = new HttpClient())
{
FileType fileExtension;
string baseUrl = "https://api-euw1.rms.com";
string apiKey = args[0];
var edmName = args[1];
var localFilePath = args[2];
// overrride baseURL if defined
if (args.Length > 3)
{
baseUrl = args[3];
}
client.BaseAddress = new Uri(baseUrl);
var bufferSizeInBytes = 20 * 1024 * 1024;
if (localFilePath.EndsWith("mdf", true, CultureInfo.InvariantCulture))
{
fileExtension= FileType.MDF;
}
else if (localFilePath.EndsWith("bak", true, CultureInfo.InvariantCulture))
{
fileExtension= FileType.BAK;
}
else
{
Console.WriteLine("Invalid File extension. Supported extensions are .mdf and .bak ");
return;
}
string fileExtensionLiteral = Enum.GetName(typeof(FileType), fileExtension).ToLowerInvariant();
//set the auth API key
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(apiKey);
var sqlInstance = await GetSqlInstance(client);
var uploadId = await GenerateUploadId(client, sqlInstance, fileExtensionLiteral, edmName);
var etags = await UploadFilesUsingMultiPartUpload(client, edmName, localFilePath, fileExtensionLiteral, bufferSizeInBytes, sqlInstance, uploadId);
await CompleteUpload(client, edmName, fileExtensionLiteral, sqlInstance, uploadId, etags);
await AttachDatabase(client, sqlInstance, edmName, fileExtension);
}
}
private static async Task AttachDatabase(HttpClient client,string instanceName, string edmName, FileType fileFormat)
{
int fileExtensionValue = (int)fileFormat;
using (HttpResponseMessage response = await client.PostAsync($"databridge/v1/sql-instances/{instanceName}/Databases/{edmName}/import?importFrom={fileExtensionValue}", null))
{
if (response.IsSuccessStatusCode)
{
using (HttpContent content = response.Content)
{
var jsonString = await content.ReadAsStringAsync();
var jsonResponse = JObject.Parse(jsonString);
string jobId = jsonResponse["jobId"].ToString();
// poll until job is complete
await PollJobStatus(client, jobId);
}
}
}
}
private static async Task PollJobStatus(HttpClient client, string jobId, int sleepIntervalInMilliseconds = 5000, int maxWaitTimeInMilliseonds = 300000)
{
string status;
int totalWaitTime = 0;
while (totalWaitTime < maxWaitTimeInMilliseonds)
{
// Query Job API
using (HttpResponseMessage response = await client.GetAsync($"databridge/v1/Jobs/{jobId}"))
{
using (HttpContent content = response.Content)
{
status = await content.ReadAsStringAsync();
if (status == "Succeeded")
{
break;
}
Thread.Sleep(sleepIntervalInMilliseconds);
totalWaitTime += sleepIntervalInMilliseconds;
}
}
}
}
private static async Task<Dictionary<string, string>> UploadFilesUsingMultiPartUpload(HttpClient client, string databaseName, string localFilePath, string fileExtension, int bufferSizeInBytes, string sqlInstanceName, string uploadId)
{
using (FileStream fileStream = File.OpenRead(localFilePath))
{
var partNumber = 1;
var etags = new Dictionary<string, string>();
while (true)
{
var buffer = new byte[bufferSizeInBytes];
var bytesRead = fileStream.Read(buffer, 0, buffer.Length); // Read a chunk from the file the size of the buffer
if (bytesRead == 0)
{
break;
}
// Get partial upload URL
var uploadUrl = await GetPresignedPartialURL(client, databaseName, fileExtension, sqlInstanceName, uploadId, partNumber);
// Upload chunk to URL
etags.Add(partNumber.ToString(), await UploadByteArray(buffer, bytesRead, uploadUrl));
partNumber++;
}
return etags;
}
}
private async static Task<string> GetPresignedPartialURL(HttpClient client, string databaseName, string fileExtension, string sqlInstanceName, string uploadId, int partNumber)
{
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, $"databridge/v1/sql-instances/{sqlInstanceName}/databases/{databaseName}/{fileExtension}/upload-part/{uploadId}/{partNumber}"))
{
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (HttpResponseMessage response = await client.SendAsync(request))
{
using (HttpContent content = response.Content)
{
var url = await content.ReadAsStringAsync();
return url.Trim('"');
}
}
}
}
private async static Task<string> UploadByteArray(byte[] buffer, int length, string uploadUrl)
{
using (HttpClient client = new HttpClient())
{
using (ByteArrayContent content = new ByteArrayContent(buffer, 0, length))
{
content.Headers.Add("Content-Type", "application/octet-stream");
using (HttpResponseMessage response = await client.PutAsync(uploadUrl, content))
{
if (response.IsSuccessStatusCode)
{
return response.Headers.ETag.Tag.Trim('"');
}
throw new Exception("Unable to upload chunk. HTTP status Code:" + response.StatusCode);
}
}
}
}
private static async Task CompleteUpload(HttpClient client, string databaseName, string fileExtension, string sqlInstanceName, string uploadId, Dictionary<string, string> etags)
{
using (HttpContent payload = new StringContent(string.Format("{{\"uploadId\": \"{0}\" , \"etags\": {1}}}", uploadId, JsonConvert.SerializeObject(etags)), Encoding.UTF8, "application/json"))
{
using (HttpResponseMessage response = await client.PostAsync($"databridge/v1/sql-instances/{sqlInstanceName}/databases/{databaseName}/{fileExtension}/complete-upload", payload))
{
if (!response.IsSuccessStatusCode)
{
throw new Exception("Unable to complete upload. HTTP status Code:" + response.StatusCode);
}
}
}
}
private static async Task<string> GenerateUploadId(HttpClient client, string sqlInstance, string fileExtension, string databaseName)
{
using (HttpResponseMessage response = await client.PostAsync($"databridge/v1/sql-instances/{sqlInstance}/databases/{databaseName}/{fileExtension}/init-upload", null))
{
if (response.IsSuccessStatusCode)
{
using (HttpContent content = response.Content)
{
var jsonString = await content.ReadAsStringAsync();
var jsonResponse = JObject.Parse(jsonString);
return jsonResponse["uploadId"].ToString();
}
}
throw new Exception("Unable to get upload ID. HTTP status Code:" + response.StatusCode);
}
}
private static async Task<string> GetSqlInstance(HttpClient client)
{
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "databridge/v1/sql-instances"))
{
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (HttpResponseMessage response = await client.SendAsync(request))
{
if (response.IsSuccessStatusCode)
{
using (HttpContent content = response.Content)
{
var jsonString = await content.ReadAsStringAsync();
var jsonResponse = JArray.Parse(jsonString);
return jsonResponse[0]["name"].ToString();
}
}
throw new Exception("Unable to get SQL insance names. HTTP status Code:" + response.StatusCode);
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;
using System.Net.Http.Headers;
using System.Globalization;
using System.Threading;
using System.Text;
namespace UploadSample
{
public class MultiPartUploadProgram
{
public enum FileType
{
MDF = 0,
BAK = 1,
DACPAC = 2
}
public static async Task Main(string[] args)
{
using (HttpClient client = new HttpClient())
{
FileType fileExtension;
string baseUrl = "https://api-euw1.rms.com";
string apiKey = args[0];
var edmName = args[1];
var localFilePath = args[2];
// overrride baseURL if defined
if (args.Length > 3)
{
baseUrl = args[3];
}
client.BaseAddress = new Uri(baseUrl);
var bufferSizeInBytes = 20 * 1024 * 1024;
if (localFilePath.EndsWith("mdf", true, CultureInfo.InvariantCulture))
{
fileExtension= FileType.MDF;
}
else if (localFilePath.EndsWith("bak", true, CultureInfo.InvariantCulture))
{
fileExtension= FileType.BAK;
}
else
{
Console.WriteLine("Invalid File extension. Supported extensions are .mdf and .bak ");
return;
}
string fileExtensionLiteral = Enum.GetName(typeof(FileType), fileExtension).ToLowerInvariant();
//set the auth API key
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(apiKey);
var sqlInstance = await GetSqlInstance(client);
var uploadId = await GenerateUploadId(client, sqlInstance, fileExtensionLiteral, edmName);
var etags = await UploadFilesUsingMultiPartUpload(client, edmName, localFilePath, fileExtensionLiteral, bufferSizeInBytes, sqlInstance, uploadId);
await CompleteUpload(client, edmName, fileExtensionLiteral, sqlInstance, uploadId, etags);
await AttachDatabase(client, sqlInstance, edmName, fileExtension);
}
}
private static async Task AttachDatabase(HttpClient client,string instanceName, string edmName, FileType fileFormat)
{
int fileExtensionValue = (int)fileFormat;
using (HttpResponseMessage response = await client.PostAsync($"databridge/v1/sql-instances/{instanceName}/Databases/{edmName}/import?importFrom={fileExtensionValue}", null))
{
if (response.IsSuccessStatusCode)
{
using (HttpContent content = response.Content)
{
var jsonString = await content.ReadAsStringAsync();
var jsonResponse = JObject.Parse(jsonString);
string jobId = jsonResponse["jobId"].ToString();
// poll until job is complete
await PollJobStatus(client, jobId);
}
}
}
}
private static async Task PollJobStatus(HttpClient client, string jobId, int sleepIntervalInMilliseconds = 5000, int maxWaitTimeInMilliseonds = 300000)
{
string status;
int totalWaitTime = 0;
while (totalWaitTime < maxWaitTimeInMilliseonds)
{
// Query Job API
using (HttpResponseMessage response = await client.GetAsync($"databridge/v1/Jobs/{jobId}"))
{
using (HttpContent content = response.Content)
{
status = await content.ReadAsStringAsync();
if (status == "Succeeded")
{
break;
}
Thread.Sleep(sleepIntervalInMilliseconds);
totalWaitTime += sleepIntervalInMilliseconds;
}
}
}
}
private static async Task<Dictionary<string, string>> UploadFilesUsingMultiPartUpload(HttpClient client, string databaseName, string localFilePath, string fileExtension, int bufferSizeInBytes, string sqlInstanceName, string uploadId)
{
using (FileStream fileStream = File.OpenRead(localFilePath))
{
var partNumber = 1;
var etags = new Dictionary<string, string>();
while (true)
{
var buffer = new byte[bufferSizeInBytes];
var bytesRead = fileStream.Read(buffer, 0, buffer.Length); // Read a chunk from the file the size of the buffer
if (bytesRead == 0)
{
break;
}
// Get partial upload URL
var uploadUrl = await GetPresignedPartialURL(client, databaseName, fileExtension, sqlInstanceName, uploadId, partNumber);
// Upload chunk to URL
etags.Add(partNumber.ToString(), await UploadByteArray(buffer, bytesRead, uploadUrl));
partNumber++;
}
return etags;
}
}
private async static Task<string> GetPresignedPartialURL(HttpClient client, string databaseName, string fileExtension, string sqlInstanceName, string uploadId, int partNumber)
{
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, $"databridge/v1/sql-instances/{sqlInstanceName}/databases/{databaseName}/{fileExtension}/upload-part/{uploadId}/{partNumber}"))
{
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (HttpResponseMessage response = await client.SendAsync(request))
{
using (HttpContent content = response.Content)
{
var url = await content.ReadAsStringAsync();
return url.Trim('"');
}
}
}
}
private async static Task<string> UploadByteArray(byte[] buffer, int length, string uploadUrl)
{
using (HttpClient client = new HttpClient())
{
using (ByteArrayContent content = new ByteArrayContent(buffer, 0, length))
{
content.Headers.Add("Content-Type", "application/octet-stream");
using (HttpResponseMessage response = await client.PutAsync(uploadUrl, content))
{
if (response.IsSuccessStatusCode)
{
return response.Headers.ETag.Tag.Trim('"');
}
throw new Exception("Unable to upload chunk. HTTP status Code:" + response.StatusCode);
}
}
}
}
private static async Task CompleteUpload(HttpClient client, string databaseName, string fileExtension, string sqlInstanceName, string uploadId, Dictionary<string, string> etags)
{
using (HttpContent payload = new StringContent(string.Format("{{\"uploadId\": \"{0}\" , \"etags\": {1}}}", uploadId, JsonConvert.SerializeObject(etags)), Encoding.UTF8, "application/json"))
{
using (HttpResponseMessage response = await client.PostAsync($"databridge/v1/sql-instances/{sqlInstanceName}/databases/{databaseName}/{fileExtension}/complete-upload", payload))
{
if (!response.IsSuccessStatusCode)
{
throw new Exception("Unable to complete upload. HTTP status Code:" + response.StatusCode);
}
}
}
}
private static async Task<string> GenerateUploadId(HttpClient client, string sqlInstance, string fileExtension, string databaseName)
{
using (HttpResponseMessage response = await client.PostAsync($"databridge/v1/sql-instances/{sqlInstance}/databases/{databaseName}/{fileExtension}/init-upload", null))
{
if (response.IsSuccessStatusCode)
{
using (HttpContent content = response.Content)
{
var jsonString = await content.ReadAsStringAsync();
var jsonResponse = JObject.Parse(jsonString);
return jsonResponse["uploadId"].ToString();
}
}
throw new Exception("Unable to get upload ID. HTTP status Code:" + response.StatusCode);
}
}
private static async Task<string> GetSqlInstance(HttpClient client)
{
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "databridge/v1/sql-instances"))
{
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (HttpResponseMessage response = await client.SendAsync(request))
{
if (response.IsSuccessStatusCode)
{
using (HttpContent content = response.Content)
{
var jsonString = await content.ReadAsStringAsync();
var jsonResponse = JArray.Parse(jsonString);
return jsonResponse[0]["name"].ToString();
}
}
throw new Exception("Unable to get SQL insance names. HTTP status Code:" + response.StatusCode);
}
}
}
}
}