I'm been reading multiples articles about the usage of the AWS S3 in Visual Studio. But all of those are focused on Web implementation.
Has anyone had the need to implement AWS S3 in WinForms - C#?
I implemented a class in C# that interact basically with AWS S3 without having too much configuration.
** I'm using the MS Framework 4.5 **
First of all, remember to use the NuGet Package and install the AWS references (In VS >> Menu >> Tools >> NuGet Package Manager >> Manage NuGet Package Manager for Solution):
AWSSDK - Amazon Simple Storage Service
AWSSDK - Core Runtime
...implemented this class, for my need I only required the access key, secret key, region and the bucket (Exists more methods, but I only required copy, read and delete).
Note: It's not required additional configuration in the app.config file or install/create a profile in the USER folder...
public class clsAwsS3
{
string accessKey { get; set; }
string secretKey { get; set; }
string bucket { get; set; }
RegionEndpoint region { get; set; }
IAmazonS3 client;
public clsAwsS3(string strBucket, string strAccessKey, string strSecretKey, RegionEndpoint region)
{
this.bucket = strBucket;
this.accessKey = strAccessKey;
this.secretKey = strSecretKey;
this.region = region;
login();
}
private void login()
{
client = new AmazonS3Client(accessKey, secretKey, region);
}
public List<string> getItems(string strPrefix = "")
{
List<string> lstResult = new List<string>();
ListObjectsV2Request listRequest;
if (strPrefix == "")
listRequest = new ListObjectsV2Request
{
BucketName = bucket
};
else
listRequest = new ListObjectsV2Request
{
BucketName = bucket,
Prefix = strPrefix
};
ListObjectsV2Response listResponse;
do
{
listResponse = client.ListObjectsV2(listRequest);
foreach (S3Object awsObject in listResponse.S3Objects)
lstResult.Add(awsObject.Key);
listRequest.ContinuationToken = listResponse.NextContinuationToken;
} while (listResponse.IsTruncated);
return lstResult;
}
public string downloadItem(string strItemKey, string strDestination)
{
GetObjectRequest request = new GetObjectRequest
{
BucketName = bucket,
Key = strItemKey
};
using (GetObjectResponse response = client.GetObject(request))
{
response.WriteResponseStreamToFile(strDestination);
}
return strDestination;
}
public void copyItem(string strItemKeySource, string strItemKeyDestination)
{
CopyObjectRequest copyRequest = new CopyObjectRequest
{
SourceBucket = bucket,
SourceKey = strItemKeySource,
DestinationBucket = bucket,
DestinationKey = strItemKeyDestination
};
CopyObjectResponse copyResponse = client.CopyObject(copyRequest);
if (copyResponse.HttpStatusCode != System.Net.HttpStatusCode.OK)
throw new Exception("Item has an error");
}
public void deleteItem(string strItemKey)
{
DeleteObjectRequest deleteObject = new DeleteObjectRequest
{
BucketName = bucket,
Key = strItemKey
};
DeleteObjectResponse deleteResponse = client.DeleteObject(deleteObject);
}
}
Hope this helps to someone else.
Related
I am working on a requirement where I want to call ARM Template dynamically from code passing parameters, like Resource Name, Resource Location etc.,
I am able to create ARM template. For example, created ARM Template for storage account and I have parameter file with values to pass to the template. However these values I want to pass from c# code dynamically and provision the resource in Azure.
From Power shell I am able to achieve this but I want the same to be done from c#.
Any suggestions/technical links that I can explore.
I use the following function to deploy an ARM Template using c#
private async Task DeployTemplate(string resourceGroupName, string deploymentName, JObject templateFileContents, JObject parameters)
{
var deployment = new Deployment
{
Properties = new DeploymentProperties
{
Mode = DeploymentMode.Incremental,
Template = templateFileContents,
Parameters = parameters
}
};
var serviceCredentials = await ApplicationTokenProvider.LoginSilentAsync(_tenantId, _clientId, _clientSecret);
var resourceManagementClient = new ResourceManagementClient(serviceCredentials)
{
SubscriptionId = _subscriptionId
};
await resourceManagementClient.Deployments.CreateOrUpdateAsync(resourceGroupName, deploymentName, deployment);
}
The templateFileContents get filled by this function:
private static JObject GetJsonFileContents(string pathToJson)
{
var path = Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location), pathToJson);
using (var file = File.OpenText(path))
{
using (var reader = new JsonTextReader(file))
{
return (JObject)JToken.ReadFrom(reader);
}
}
}
This is how I create a dynamic parameters object:
var parameters = JObject.FromObject(
new DbCloneArmParameters
{
databaseName = new ArmParameterValue { Value = databaseName },
serverName = new ArmParameterValue { Value = servername },
location = new ArmParameterValue { Value = "westeurope" } }
});
Helper classes:
public class DbCloneArmParameters
{
public ArmParameterValue databaseName { get; set; }
public ArmParameterValue serverName { get; set; }
public ArmParameterValue location { get; set; }
}
public class ArmParameterValue
{
public string Value { get; set; }
}
I think Microsoft.Azure.Management.Fluent solves your problem.
If you look at Microsoft documentation "Deploy an Azure Virtual Machine using C# and a Resource Manager template", it walks you through deploying a VM using a template file from C#. The documentation could be used as a starting point to deploy any ARM template to Azure.
[Note: I've already worked out an answer to this but struggled to find anything online so I'm adding it here]
I need to invalidate the cache for an individual AWS API Gateway endpoint using ASPNETCORE.
The docs say to send a signed request. How do you do this in .NET?
I'm answering my own question as I couldn't find much information online and it took a bit of time to get working. Hopefully it'll help someone.
I've added code here: https://gist.github.com/secretorange/905b4811300d7c96c71fa9c6d115ee24
CacheInvalidationRequestBuilder.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
namespace Aws
{
public static class CacheInvalidationRequestBuilder
{
private const string ServiceName = "execute-api";
private const string Algorithm = "AWS4-HMAC-SHA256";
private const string ContentType = "application/json";
private const string DateTimeFormat = "yyyyMMddTHHmmssZ";
private const string DateFormat = "yyyyMMdd";
public static WebRequest Build(CacheInvalidationRequestModel request)
{
string hashedRequestPayload = CreateRequestPayload(String.Empty);
string authorization = Sign(request, hashedRequestPayload, "GET", request.AbsolutePath, request.QueryString);
string requestDate = DateTime.UtcNow.ToString(DateTimeFormat);
var webRequest = WebRequest.Create($"https://{request.Host}{request.AbsolutePath}");
webRequest.Method = "GET";
webRequest.ContentType = ContentType;
webRequest.Headers.Add("Cache-Control", "max-age=0");
webRequest.Headers.Add("Host", request.Host);
webRequest.Headers.Add("X-Amz-Date", requestDate);
webRequest.Headers.Add("Authorization", authorization);
return webRequest;
}
private static string CreateRequestPayload(string jsonString)
{
return HexEncode(Hash(ToBytes(jsonString)));
}
private static string Sign(CacheInvalidationRequestModel request, string hashedRequestPayload, string requestMethod, string canonicalUri, string canonicalQueryString)
{
var currentDateTime = DateTime.UtcNow;
var dateStamp = currentDateTime.ToString(DateFormat);
var requestDate = currentDateTime.ToString(DateTimeFormat);
var credentialScope = $"{dateStamp}/{request.Region}/{ServiceName}/aws4_request";
var headers = new SortedDictionary<string, string> {
{ "cache-control", "max-age=0" },
{ "content-type", ContentType },
{ "host", request.Host },
{ "x-amz-date", requestDate }
};
var canonicalHeaders = string.Join("\n", headers.Select(x => x.Key.ToLowerInvariant() + ":" + x.Value.Trim())) + "\n";
// Task 1: Create a Canonical Request For Signature Version 4
var SignedHeaders = String.Join(';', headers.Select(x => x.Key.ToLowerInvariant()));
var canonicalRequest = $"{requestMethod}\n{canonicalUri}\n{canonicalQueryString}\n{canonicalHeaders}\n{SignedHeaders}\n{hashedRequestPayload}";
var hashedCanonicalRequest = HexEncode(Hash(ToBytes(canonicalRequest)));
// Task 2: Create a String to Sign for Signature Version 4
var stringToSign = $"{Algorithm}\n{requestDate}\n{credentialScope}\n{hashedCanonicalRequest}";
// Task 3: Calculate the AWS Signature Version 4
var signingKey = GetSignatureKey(request.SecretKey, dateStamp, request.Region, ServiceName);
var signature = HexEncode(HmacSha256(stringToSign, signingKey));
// Task 4: Prepare a signed request
// Authorization: algorithm Credential=access key ID/credential scope, SignedHeadaers=SignedHeaders, Signature=signature
var authorization = $"{Algorithm} Credential={request.AccessKey}/{dateStamp}/{request.Region}/{ServiceName}/aws4_request, SignedHeaders={SignedHeaders}, Signature={signature}";
return authorization;
}
private static byte[] GetSignatureKey(string key, string dateStamp, string regionName, string serviceName)
{
var kDate = HmacSha256(dateStamp, ToBytes("AWS4" + key));
var kRegion = HmacSha256(regionName, kDate);
var kService = HmacSha256(serviceName, kRegion);
return HmacSha256("aws4_request", kService);
}
private static byte[] ToBytes(string str)
{
return Encoding.UTF8.GetBytes(str.ToCharArray());
}
private static string HexEncode(byte[] bytes)
{
return BitConverter.ToString(bytes).Replace("-", string.Empty).ToLowerInvariant();
}
private static byte[] Hash(byte[] bytes)
{
return SHA256.Create().ComputeHash(bytes);
}
private static byte[] HmacSha256(string data, byte[] key)
{
return new HMACSHA256(key).ComputeHash(ToBytes(data));
}
}
}
CacheInvalidationRequestModel.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace Aws
{
public class CacheInvalidationRequestModel
{
public string Region { get; set; }
public string Host { get; set; }
public string AbsolutePath { get; set; }
public string QueryString { get; set; }
public string AccessKey { get; set; }
public string SecretKey { get; set; }
}
}
How to use the code
To make a request, use code similar to:
var url = $"/myendpoint";
var model = GetCacheInvalidationRequestModel(url);
var request = CacheInvalidationRequestBuilder.Build(model);
try
{
// Hit the endpoint
using (var response = request.GetResponse())
{
// Not currently doing anything with the response
}
}
catch(Exception ex)
{
Logger.LogError(ex, "Problem invalidating cache for url: " + url);
}
The GetCacheInvalidationRequestModel method might look something like this (I pass in the model properties as IOptions):
private CacheInvalidationRequestModel GetCacheInvalidationRequestModel(string absolutePath)
{
return new CacheInvalidationRequestModel()
{
Region = Options.Region,
Host = Options.Host,
AccessKey = Options.InvalidatorKey,
SecretKey = Options.InvalidatorSecret,
AbsolutePath = absolutePath
};
}
AWS Information
AWS docs for building signed requests are here: https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
Your AWS user will need an attached policy, as shown here: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-caching.html
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"execute-api:InvalidateCache"
],
"Resource": [
"arn:aws:execute-api:region:account-id:api-id/stage-name/GET/resource-path-specifier"
]
}
]
}
NOTE: You can use wildcards if you like.
I need help with uploading files to Slack.
I have a Slack-App that is working with my code(below) so far. But all I can do is post messages. I can not attach images to the messages - because I do not understand how to use the so called "methods" and the syntax Slack is "showing" on their API-page.
This creates my "content" and below its just a Stream for reading a file I could upload:
public class PostMessage
{
public FormUrlEncodedContent Content(string message, string file)
{
var values = new Dictionary<string, string>
{
{"token", "xoxp-myToken"},
{ "username", "X"},
{ "channel", "myChannel"},
{ "as_user", "false"},
{"text", message},
{ "content", file},
{ "attachments","[{ \"fallback\":\"dummy\", \"text\":\"this is a waste of time\"}]"}
};
var content = new FormUrlEncodedContent(values);
return content;
}
}
public class PostFile
{
String path = #"C:\Users\f.held\Desktop\Held-Docs\dagged.jpg";
public string ReadImageFile()
{
FileInfo fileInfo = new FileInfo(path);
long imageFileLength = fileInfo.Length;
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
BinaryReader br = new BinaryReader(fs);
byte[] imageData = br.ReadBytes((int)imageFileLength);
var str = Encoding.Default.GetString(imageData);
return str;
}
}
}
The client that communicates:
public class SlackClient
{
private readonly Uri _webhookUrl;
private readonly HttpClient _httpClient = new HttpClient {};
public SlackClient(Uri webhookUrl)
{
_webhookUrl = webhookUrl;
}
public async Task<HttpResponseMessage> SendMessageAsync(FormUrlEncodedContent content)
{
var response = await _httpClient.PostAsync(_webhookUrl, content);
return response;
}
}
}
The Main:
public static void Main(string[] args)
{
Task.WaitAll(IntegrateWithSlackAsync());
}
private static async Task IntegrateWithSlackAsync()
{
var webhookUrl = new Uri("https://slack.com/api/files.upload");
var slackClient = new SlackClient(webhookUrl);
PostMessage PM = new PostMessage();
PostFile PF = new PostFile();
while (true)
{
Console.Write("Type a message: ");
var message = Console.ReadLine();
var testFile = PF.ReadImageFile();
FormUrlEncodedContent payload = PM.Content(message, testFile);
var response = await slackClient.SendMessageAsync(payload);
var isValid = response.IsSuccessStatusCode ? "valid" : "invalid";
Console.WriteLine($"Received {isValid} response.");
Console.WriteLine(response);
response.Dispose();
}
}
}
}
If somebody has an example on what a upload has to look like. Or even better,
if somebody could really explain the syntax these Slack-Messages have to have.
That would be great! I still do not know where and HOW I should put the so called
"Accepted content types: multipart/form-data, application/x-www-form-urlencoded" to my upload. I just can not find examples on this...
Edit:
What confuses me needlesly is that Slack states they have an extra method called file.upload - but we shouldn't use it anymore, we should use just postMessage.
But how would I "pack" a file in a message? My syntax always seems to be off. Especially when it comes to "content"...
I just can not figure out what the c#-code has to look like. Where do I declare the aforementioned "content type"?
Another problem is, it always sends my messages through - means I get a 200-response from the server. But it never shows the file (which probably means the syntax is off) Or I get the 200-response but the message never shows in Slack.
Images in message
If you want to include an image in your message (along with some text) you can do so by adding images as message attachment to a normal message send with chat.postMessage.
For that you need a public URL of your image and that link with the image_url property to an attachment. That attachment can also contain text, and you can add multiple attachments to your message.
This is how it looks like:
And here is how this message looks in JSON:
{
"channel": "test",
"text": "This is a message example with images in the attachment",
"attachments": [
{
"fallback": "game over",
"text": "This is some text in the attachement",
"image_url": "https://i.imgur.com/jO9N3eJ.jpg"
}
]
}
Uploading images
The image URL needs to be publicly accessible on the Internet. So you need to host your image file on a public webserver or upload it to a image cloud service (e.g. imgur.com).
You can also use Slack as cloud service for your images. Here is how that works:
Upload to Slack: Upload your image to your Slack workspace with files.upload
Get public URL: Get a public URL for your image file with files.sharedPublicURL. Normally all files on Slack are private, but you can only use public URLs for message attachments.
Send message: Include your image as attachment in a message: Use the permalink_public property of your image file as value for image_url
Example code
Here is a full working example in C# for first uploading an image to Slack and then using it in a message.
Note: This example requires Newtonsoft.Json.
using System;
using System.Net;
using System.Collections.Specialized;
using System.Text;
using Newtonsoft.Json;
public class SlackExample
{
// classes for converting JSON respones from API method into objects
// note that only those properties are defind that are needed for this example
// reponse from file methods
class SlackFileResponse
{
public bool ok { get; set; }
public String error { get; set; }
public SlackFile file { get; set; }
}
// a slack file
class SlackFile
{
public String id { get; set; }
public String name { get; set; }
public String permalink_public { get; set; }
}
// reponse from message methods
class SlackMessageResponse
{
public bool ok { get; set; }
public String error { get; set; }
public String channel { get; set; }
public String ts { get; set; }
}
// a slack message attachment
class SlackAttachment
{
public String fallback { get; set; }
public String text { get; set; }
public String image_url { get; set; }
}
// main method with logic
public static void Main()
{
String token = "xoxp-YOUR-TOKEN";
/////////////////////
// Step 1: Upload file to Slack
var parameters = new NameValueCollection();
// put your token here
parameters["token"] = token;
var client1 = new WebClient();
client1.QueryString = parameters;
byte[] responseBytes1 = client1.UploadFile(
"https://slack.com/api/files.upload",
"C:\\Temp\\Stratios_down.jpg"
);
String responseString1 = Encoding.UTF8.GetString(responseBytes1);
SlackFileResponse fileResponse1 =
JsonConvert.DeserializeObject<SlackFileResponse>(responseString1);
String fileId = fileResponse1.file.id;
/////////////////////
// Step 2: Make file public and get the URL
var parameters2 = new NameValueCollection();
parameters2["token"] = token;
parameters2["file"] = fileId;
var client2 = new WebClient();
byte[] responseBytes2 = client2.UploadValues("https://slack.com/api/files.sharedPublicURL", "POST", parameters2);
String responseString2 = Encoding.UTF8.GetString(responseBytes2);
SlackFileResponse fileResponse2 =
JsonConvert.DeserializeObject<SlackFileResponse>(responseString2);
String imageUrl = fileResponse2.file.permalink_public;
/////////////////////
// Step 3: Send message including freshly uploaded image as attachment
var parameters3 = new NameValueCollection();
parameters3["token"] = token;
parameters3["channel"] = "test_new";
parameters3["text"] = "test message 2";
// create attachment
SlackAttachment attachment = new SlackAttachment();
attachment.fallback = "this did not work";
attachment.text = "this is anattachment";
attachment.image_url = imageUrl;
SlackAttachment[] attachments = { attachment };
parameters3["attachments"] = JsonConvert.SerializeObject(attachments);
var client3 = new WebClient();
byte[] responseBytes3 = client3.UploadValues("https://slack.com/api/chat.postMessage", "POST", parameters3);
String responseString3 = Encoding.UTF8.GetString(responseBytes3);
SlackMessageResponse messageResponse =
JsonConvert.DeserializeObject<SlackMessageResponse>(responseString3);
}
}
Here is a shorter working example showing how to just upload any file to Slack with C# only. The example will also automatically share the file the given channel.
I have included the logic to convert the API response from JSON, which will always be needed to determine if the API call was successful.
Note: This example requires Newtonsoft.Json
using System;
using System.Net;
using System.Collections.Specialized;
using System.Text;
using Newtonsoft.Json;
public class SlackExample
{
// classes for converting JSON respones from API method into objects
// note that only those properties are defind that are needed for this example
// reponse from file methods
class SlackFileResponse
{
public bool ok { get; set; }
public String error { get; set; }
public SlackFile file { get; set; }
}
// a slack file
class SlackFile
{
public String id { get; set; }
public String name { get; set; }
}
// main method with logic
public static void Main()
{
var parameters = new NameValueCollection();
// put your token here
parameters["token"] = "xoxp-YOUR-TOKEN";
parameters["channels"] = "test";
var client = new WebClient();
client.QueryString = parameters;
byte[] responseBytes = client.UploadFile(
"https://slack.com/api/files.upload",
"D:\\temp\\Stratios_down.jpg"
);
String responseString = Encoding.UTF8.GetString(responseBytes);
SlackFileResponse fileResponse =
JsonConvert.DeserializeObject<SlackFileResponse>(responseString);
}
}
About content types: Those are part of the header of a HTTP request and can be set manually in the WebClient object (see also this answer). However, for our case you can ignore it, because the default content types that WebClient is using for the POST request will work just fine.
Also see this answer on how to upload files with the WebClient class.
Here is another complete example for uploading a file to Slack, this time using the async approach with HttpClient.
Note: This example requires Newtonsoft.Json.
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
namespace SlackExample
{
class UploadFileExample
{
private static readonly HttpClient client = new HttpClient();
// classes for converting JSON respones from API method into objects
// note that only those properties are defind that are needed for this example
// reponse from file methods
class SlackFileResponse
{
public bool ok { get; set; }
public String error { get; set; }
public SlackFile file { get; set; }
}
// a slack file
class SlackFile
{
public String id { get; set; }
public String name { get; set; }
}
// sends a slack message asynchronous
// throws exception if message can not be sent
public static async Task UploadFileAsync(string token, string path, string channels)
{
// we need to send a request with multipart/form-data
var multiForm = new MultipartFormDataContent();
// add API method parameters
multiForm.Add(new StringContent(token), "token");
multiForm.Add(new StringContent(channels), "channels");
// add file and directly upload it
FileStream fs = File.OpenRead(path);
multiForm.Add(new StreamContent(fs), "file", Path.GetFileName(path));
// send request to API
var url = "https://slack.com/api/files.upload";
var response = await client.PostAsync(url, multiForm);
// fetch response from API
var responseJson = await response.Content.ReadAsStringAsync();
// convert JSON response to object
SlackFileResponse fileResponse =
JsonConvert.DeserializeObject<SlackFileResponse>(responseJson);
// throw exception if sending failed
if (fileResponse.ok == false)
{
throw new Exception(
"failed to upload message: " + fileResponse.error
);
}
else
{
Console.WriteLine(
"Uploaded new file with id: " + fileResponse.file.id
);
}
}
static void Main(string[] args)
{
// upload this file and wait for completion
UploadFileAsync(
"xoxp-YOUR-TOKEN",
"C:\\temp\\Stratios_down.jpg",
"test"
).Wait();
Console.ReadKey();
}
}
}
good morning, I have a solution in .net where I call the webservices of saber bargain finder max. Now I want to download the compressed information but the response object returns null. I read that you have to call the interface IClientMessageInspector BeforeSendRequest and AfterReceiveReply but I do not know how to proceed. someone will have an example or solution about it? Thank you
The response for the service cannot handle the compressed response, so yes, you have to put middleware in order to process the decompression before you let it continue.
First of all, you need to import the BFM WSDL as a service instead of as a web services, so:
Right click on "Service References" and click on "Add Service Reference..."
Paste the WSDL URL under "Address:" and click on "Go"
Recommendation: Download the WSDL and the associated schemas so you can modify them, since .NET has some issues handling some things, which you can manually modify. Sabre has some covered here.
Name the service whatever you like, in this example I'll call it BargainFinderMaxRQ_4_1_0_Srvc, under "Namespace:" and click on "OK"
Then, I created 2 classes, one that will be the one calling BFM ("BFM_v410Service"), and another one for the middleware ("BFMInspector").
Let's start with the BFMInspector:
// The inspector class has to implement both IClientMessageInspector and IEndpointBehavior interfaces
public class BFMInspector : IClientMessageInspector, IEndpointBehavior
{
// This is the method to action after receiving a response from Sabre
public void AfterReceiveReply(ref Message reply, object correlationState)
{
try
{
// Get compressed response from reply and load that into a byte array.
XmlDictionaryReader bodyReader = reply.GetReaderAtBodyContents();
bodyReader.ReadStartElement("CompressedResponse");
byte[] bodyByteArray = bodyReader.ReadContentAsBase64();
// Create some helper variables
StringBuilder uncompressed = new StringBuilder();
String xmlString = "";
XmlDocument xmlPayload = new XmlDocument();
// Load the byte array into memory
using (MemoryStream memoryStream = new MemoryStream(bodyByteArray))
{
// Unzip the Stream
using (GZipStream gZipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
{
byte[] buffer = new byte[1024];
int readBytes;
// Unzips character by character
while ((readBytes = gZipStream.Read(buffer, 0, buffer.Length)) != 0)
{
for (int i = 0; i < readBytes; i++)
// Append all characters to build the response
uncompressed.Append((char)buffer[i]);
}
}
xmlString = uncompressed.ToString();
xmlString = xmlString.Replace("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>", "");
}
// Convert the string into an XML
xmlPayload.LoadXml(xmlString);
// Create a new Message, which is what will substitute what was returned by Sabre
Message tempMessage = Message.CreateMessage(reply.Version, null, xmlPayload.ChildNodes[0]);
tempMessage.Headers.CopyHeadersFrom(reply.Headers);
tempMessage.Properties.CopyProperties(reply.Properties);
MessageBuffer bufferOfFault = tempMessage.CreateBufferedCopy(Int32.MaxValue);
// Replace the reply with the new Message
reply = bufferOfFault.CreateMessage();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
// Nothing is done here, so we simply return null
return null;
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
// Nothing done
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
// Add "this" as an endpoint to be inspected
clientRuntime.MessageInspectors.Add(this);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
// Nothing done
}
public void Validate(ServiceEndpoint endpoint)
{
// Nothing done
}
}
Now, the BFM_v410Service:
// BFM calling class
public class BFM_v410Service
{
// The constructor and CreateRequest simeply create a complete request
private BargainFinderMaxRQRequest service;
private OTA_AirLowFareSearchRQ request;
public OTA_AirLowFareSearchRS response;
private string endpoint;
public BFM_v410Service(string token, string pcc, string convId, string endpoint)
{
CreateRequest(pcc, true);
this.endpoint = endpoint;
service = new BargainFinderMaxRQRequest()
{
MessageHeader = new BargainFinderMaxRQ_3_4_0_Srvc.MessageHeader()
{
From = new From()
{
PartyId = new PartyId[]
{
new PartyId()
{
Value = pcc
}
}
},
To = new To()
{
PartyId = new PartyId[]
{
new PartyId()
{
Value = endpoint
}
}
},
ConversationId = convId,
CPAId = pcc,
Service = new Service()
{
Value = "BargainFinderMaxRQ"
},
Action = "BargainFinderMaxRQ",
MessageData = new MessageData()
{
Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssK")
}
},
OTA_AirLowFareSearchRQ = request,
Security = new Security()
{
BinarySecurityToken = token
}
};
}
private void CreateRequest(string pcc, bool compressed)
{
request = new OTA_AirLowFareSearchRQ()
{
Version = "3.4.0",
POS = new SourceType[]
{
new SourceType()
{
PseudoCityCode = pcc,
RequestorID = new UniqueID_Type()
{
ID = "1",
Type = "1",
CompanyName = new CompanyNameType()
{
Code = "TN",
Value = "TN"
}
}
}
},
OriginDestinationInformation = new OTA_AirLowFareSearchRQOriginDestinationInformation[]
{
new OTA_AirLowFareSearchRQOriginDestinationInformation()
{
RPH = "1",
Item = "2018-09-21T11:00:00",
ItemElementName = ItemChoiceType.DepartureDateTime,
OriginLocation = new OriginDestinationInformationTypeOriginLocation()
{
LocationCode = "MVD"
},
DestinationLocation = new OriginDestinationInformationTypeDestinationLocation()
{
LocationCode = "KRK"
}
},
new OTA_AirLowFareSearchRQOriginDestinationInformation()
{
RPH = "2",
Item = "2018-09-28T11:00:00",
ItemElementName = ItemChoiceType.DepartureDateTime,
OriginLocation = new OriginDestinationInformationTypeOriginLocation()
{
LocationCode = "KRK"
},
DestinationLocation = new OriginDestinationInformationTypeDestinationLocation()
{
LocationCode = "MVD"
}
}
},
TravelerInfoSummary = new TravelerInfoSummaryType()
{
AirTravelerAvail = new TravelerInformationType[]
{
new TravelerInformationType()
{
PassengerTypeQuantity = new PassengerTypeQuantityType[]
{
new PassengerTypeQuantityType()
{
Code = "ADT",
Quantity = "1"
}
}
}
}
},
TPA_Extensions = new OTA_AirLowFareSearchRQTPA_Extensions()
{
IntelliSellTransaction = new TransactionType()
{
RequestType = new TransactionTypeRequestType()
{
Name = "50ITINS"
},
CompressResponse = new TransactionTypeCompressResponse()
{
Value = compressed
}
}
}
};
}
public void Execute()
{
try
{
// Instanciate the Inspector
BFMInspector inspector = new BFMInspector();
// Select the URL you'll be sending the request. I've passed this as a parameter in the constructor
EndpointAddress url = new EndpointAddress(new Uri(endpoint));
// Create a binding, with a couple of characteristics, because of the size of the response
Binding binding = new BasicHttpsBinding()
{
MaxReceivedMessageSize = Int32.MaxValue,
MaxBufferSize = Int32.MaxValue
};
// Create the executable the BargainFinderMaxPortTypeClient variable, which will allow me to call the service
BargainFinderMaxPortTypeClient execute = new BargainFinderMaxPortTypeClient(binding, url);
// Add the middleware. Here's where ApplyClientBehavior is called behind the scene and adds itself
execute.Endpoint.EndpointBehaviors.Add(inspector);
// Call BFM and successfully get the response as an OTA_AirLowFareSearchRS object
response = execute.BargainFinderMaxRQ(ref service.MessageHeader, ref service.Security, request);
Console.WriteLine(response);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Then, simply call this from a console application:
static void Main(string[] args)
{
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
string token = #"Shared/IDL:IceSess\/SessMgr:1\.0.IDL/Common/!ICESMS\/RESA!ICESMSLB\/RES.LB!-3146624380791354996!413892!0!1!E2E-1";
string pcc = "XXXX";
string convId = "HERE GOES YOUR CONVERSATION ID";
string endpoint = "https://webservices.havail.sabre.com";
BFM_v410Service bfm340 = new BFM_v410Service(token, pcc, convId, endpoint);
bfm410.Execute();
}
Hope this helps!
Using c# with the .net framework and microsoft azure and i am trying to upload a file in a cli which should be picked up by a webjob. I'm sure the webjob is fine but i am having problems getting upload to work.
// Pick URL location of service up from metadata
AudioSamples client = new AudioSamples(new AnonymousCredential());
var id = Console.ReadLine();
Console.WriteLine();
string path = "api/samples/" + id;
Console.WriteLine("Enter the file name.");
string fileName = Console.ReadLine();
using (var stream = File.OpenRead(fileName))
{
HttpOperationResponse response = await client.PutAsync(path, new StreamContent(stream));
}
From what i understand the PutAsync should work for streaming the file but it gives me an error saying the command doesn't exist
this api should also be used with the upload to a blob but im not sure how it connect it to the client.
namespace AudioSamples.Controllers
{
[ApiExplorerSettings(IgnoreApi = true)]
public class DataController : ApiController
{
private const String partitionName = "AudioSamples_Partition_1";
private CloudStorageAccount storageAccount;
private CloudTableClient tableClient;
private CloudTable table;
private BlobStorageService _blobStorageService = new BlobStorageService();
private CloudQueueService _queueStorageService = new CloudQueueService();
String name;
public DataController()
{
storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["AzureWebJobsStorage"].ToString());
tableClient = storageAccount.CreateCloudTableClient();
table = tableClient.GetTableReference("AudioSamples");
}
private void deleteOldBlobs(AudioSampleEntityModel sample)
{
CloudBlobContainer blobContainer = _blobStorageService.getCloudBlobContainer();
CloudBlockBlob blob;
if (sample.Blob != null)
{
blob = blobContainer.GetBlockBlobReference(sample.Blob);
blob.Delete();
}
if (sample.SampleBlob != null)
{
blob = blobContainer.GetBlockBlobReference(sample.SampleBlob);
blob.Delete();
}
}
// PUT: api/Data/5
public IHttpActionResult Put(String id)
{
// PUT – see also ProductsController.cs from Lab 4
// Create a retrieve operation.
// id is a parameter of method and forms the row key
TableOperation retrieveOperation =
TableOperation.Retrieve<AudioSampleEntityModel>(partitionName, id);
TableResult getOperationResult = table.Execute(retrieveOperation);
if (getOperationResult.Result == null)
return NotFound();
AudioSampleEntityModel sample = (AudioSampleEntityModel)getOperationResult.Result;
deleteOldBlobs(sample);
try
{
CloudBlobContainer blobContainer = _blobStorageService.getCloudBlobContainer();
name = string.Format("{0}{1}", Guid.NewGuid(), ".mp3");
String path = "/mp3s" + name;
var baseUrl = Request.RequestUri.GetLeftPart(UriPartial.Authority);
String sampleURL = baseUrl.ToString() + "/api/data/" + id;
sample.SampleBlobURL = sampleURL;
sample.Blob = path;
sample.CreatedDate = DateTime.Now;
sample.SampleDate = null;
var updateOperation = TableOperation.InsertOrReplace(sample);
table.Execute(updateOperation);
CloudBlockBlob blob = blobContainer.GetBlockBlobReference(sample.Blob);
var request = HttpContext.Current.Request;
blob.Properties.ContentType = "audio/mpeg3";
blob.UploadFromStream(request.InputStream);
}
catch (Exception e)
{
System.Diagnostics.Trace.WriteLine("DataController(PUT): " + e.Message);
return BadRequest("DataController(PUT): " + e.Message);
}
try
{
CloudQueue sampleQueue = _queueStorageService.getCloudQueue();
var queueMessageSample = new AudioSampleEntityModel(partitionName, id);
sampleQueue.AddMessage(new CloudQueueMessage(JsonConvert.SerializeObject(queueMessageSample)));
}
catch (Exception e)
{
System.Diagnostics.Trace.WriteLine("DataController(PUT): " + e.Message);
return BadRequest("DataController(PUT): " + e.Message);
}
System.Diagnostics.Trace.WriteLine(String.Format("*** WebRole: Enqueued '{0}'", sample.Blob));
return StatusCode(HttpStatusCode.NoContent);
}
}
}
This is the webjob which i think is working fine.
public class Functions
{
public static void GenerateSample(
[QueueTrigger("audiosamplemaker")] AudioSampleEntityModel sampleInQueue,
[Table("Samples", "{PartitionKey}", "{RowKey}")] AudioSampleEntityModel sampleInTable,
[Blob("audiocollection/audio/{queueTrigger}")] CloudBlockBlob inputBlob,
[Blob("audiocollection/samples/{queueTrigger}")] CloudBlockBlob outputBlob, TextWriter logger,
[Table("Samples")] CloudTable tableBinding, TextWriter kek)
{
//use log.WriteLine() rather than Console.WriteLine() for trace output
logger.WriteLine("GenerateSample() started...");
logger.WriteLine("Input blob is: " + sampleInQueue);
// Open streams to blobs for reading and writing as appropriate.
// Pass references to application specific methods
using (Stream input = inputBlob.OpenRead())
using (Stream output = outputBlob.OpenWrite())
{
createSample(input, output, 20);
outputBlob.Properties.ContentType = "audio/mp3";
outputBlob.Metadata["Title"] = inputBlob.Metadata["Title"];
}
logger.WriteLine("GenerateSample() completed...");
}
private static void createSample(Stream input, Stream output, int duration)
{
using (var reader = new Mp3FileReader(input, wave => new NLayer.NAudioSupport.Mp3FrameDecompressor(wave)))
{
Mp3Frame frame;
frame = reader.ReadNextFrame();
int frameTimeLength = (int)(frame.SampleCount / (double)frame.SampleRate * 1000.0);
int framesRequired = (int)(duration / (double)frameTimeLength * 1000.0);
int frameNumber = 0;
while ((frame = reader.ReadNextFrame()) != null)
{
frameNumber++;
if (frameNumber <= framesRequired)
{
output.Write(frame.RawData, 0, frame.RawData.Length);
}
else break;
}
}
}
}
In your web api controller, the put method only has one parameter named ‘id’. But in your client, you also passed a stream content in put method.
So you couldn’t call web api method successfully. And for this stream content, you could set a string or model type in Put method instead of file stream.
According to your description, I suppose you want to pass data from client to web api, then read data from this file path in web Api and upload data to a blob.
I have created a sample demo to upload a myfile.txt to a Blob. Other data types are similar to this. You could refer to.
Code in Console:
Code in AudioSampleEntityModel class:
public class AudioSampleEntityModel : TableEntity
{
public AudioSampleEntityModel()
{
}
public AudioSampleEntityModel(string partitionName, string id)
{
}
public string id { get; set; }
public string Blob { get; set; }
public string SampleBlob { get; set; }
public string SampleBlobURL { get; set; }
public DateTime CreatedDate { get; set; }
public string SampleDate { get; set; }
}
Code in Function.cs:
public class Functions
{
/// <summary>
/// pass the content from Queue to Blob(The content is from myfile.txt)
/// </summary>
/// <param name="model"></param>
/// <param name="orderBlob"></param>
public static void MultipleOutput(
[QueueTrigger("myqueue2")] AudioSampleEntityModel model,
[Blob("orders/myfile")] out string orderBlob) //create a container named 'orders'
{
orderBlob = model.SampleBlob; //store the content from SampleBlob property to Blob
}
}
Code in Program:
class Program
{
static void Main() {
Console.WriteLine("----------------Update Employee -------------------");
Console.WriteLine("Enter id which you want to update");
string id = Console.ReadLine();
var response=DemoData(id).Result;
Console.WriteLine("the data from webapi: "+response);
var host = new JobHost();
// The following code ensures that the WebJob will be running continuously
host.RunAndBlock();
}
public static async Task<string> DemoData(string id)
{
HttpClient client = new HttpClient();
string path = "http://localhost:53581/api/values/" + id;
Console.WriteLine("Enter the file name.");
string fileName = Console.ReadLine();
string filepath = "E:\\JanleyZhang\\" + fileName;
var filepathJson = JsonConvert.SerializeObject(filepath);// convert other FileStream type to json string
var data = new StringContent(content: filepathJson,
encoding: Encoding.UTF8,
mediaType: "application/json");
var response = await client.PutAsync(path, data);//get response from web api
var content = response.Content;
var result = await content.ReadAsStringAsync();//read content from response
//upload the data to a queue
string connectionString = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString);
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();
CloudQueue queue = queueClient.GetQueueReference("myqueue2");
queue.CreateIfNotExists();
AudioSampleEntityModel model = new AudioSampleEntityModel()
{
id=id,
SampleBlob=result //pass the result to this property
};
queue.AddMessage(new CloudQueueMessage(JsonConvert.SerializeObject(model))); //store the file content to queue
return result;
}
}
Code in api controller:
public string Put(string id, [FromBody]string filepath) //pass two parameters
{
string b = "The id and model id are not equal.";
if (id == "1")
{
FileStream fs = File.OpenRead(filepath);
byte[] byt = new byte[fs.Length];
UTF8Encoding temp = new UTF8Encoding(true);
while (fs.Read(byt, 0, byt.Length) > 0)
{
b=temp.GetString(byt);//read the content from myfile.txt
// The operation about uploading a blob .........
}
return b;
}
return b;
}
You could see the result like this: