How to handle png from rest api? - c#

Right now, I'm creating a WCF service that sends a location to the Bing Map API and returns a PNG image to the service client. Currently, I copied a working api example from their documentation webpage, and I'm having a hard time figuring out how I can pass it on.
From other stackoverflow questions, I started by converting the response, and I got it into Base64. But it triggered and received an error, that says input is not in Base64 form.
A screenshot of what input looks like
public string getResponse()
{
string key = [My Api Key];
Uri geocodeRequest = new Uri(string.Format("http://dev.virtualearth.net/REST/v1/Locations?q={0}&key={1}", query, key));
Uri imageryRequest = new Uri(string.Format("https://dev.virtualearth.net/REST/v1/Imagery/Map/Road/Redmond Washington?ms=500,270&zl=12&&c=en-US&he=1&key={0}", key));
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(imageryRequest);
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
//Handling the response in PNG
Stream stream = response.GetResponseStream();
StreamReader reader = new StreamReader(stream);
string input = reader.ReadToEnd();
byte[] data = convert.FromBase64String(input);
return data;
}

It already is a PNG image as byte[]. Also, keep in mind that the image returned is not guaranteed to be PNG, JPEG, or GIF. It returns what it feels is the most appropriate image type unless a specific type is requested.
ex. fmt=jpeg
You just need to do something with it. In my example, I saved it to a file. You probably just need to return the byte[].
private static HttpClient client = new HttpClient();
public static async void GetResponse()
{
string key = Properties.Settings.Default.Key;
Uri imgUri = new Uri($"https://dev.virtualearth.net/REST/v1/Imagery/Map/Road/Redmond Washington?ms=500,270&zl=12&&c=en-US&he=1&fmt=png&key={key}");
HttpResponseMessage response = await client.GetAsync(imgUri);
response.EnsureSuccessStatusCode();
byte[] responseData = await response.Content.ReadAsByteArrayAsync();
File.WriteAllBytes("test.png", responseData);
}

Related

C# HttpClient save response with MIME "text/plain" as an UTF-8 string

I'm sending a request with HttpClient to a remote endpoint. I want to download the content and save it to a file as an UTF-8 string.
If the server would respond with the proper Content-Type text/plain; charset=utf-8, then the following code processes it just fine:
HttpClient client = new();
HttpResponseMessage res = await client.GetAsync(url);
string text = await res.Content.ReadAsStringAsync();
File.WriteAllText("file.txt", text);
However, the server always returns the basic Content-Type text/plain and I'm unable to get that as an UTF-8 string.
HttpClient cl = new();
HttpResponseMessage res = await cl.GetAsync(url);
string attempt1 = await res.Content.ReadAsStringAsync();
string attempt2 = Encoding.UTF8.GetString(await res.Content.ReadAsByteArrayAsync());
Stream stream = await res.Content.ReadAsStreamAsync();
byte[] bytes = ((MemoryStream)stream).ToArray();
string attempt3 = Encoding.UTF8.GetString(bytes);
I tried all three of these approaches, all resulted in scrambled characters due to the encoding mismatch. I don't have control over the server, so I can't change the headers.
Is there any way to force HttpClient to parse it as UTF-8? Why are the manual approaches not working?
I've built a Cloudflare worker to demonstrate this behavior and allow you to easily debug:
https://headers.briganreiz.workers.dev/charset-in-header
https://headers.briganreiz.workers.dev/no-charset
Edit: Turns out it was the GZip compression on the main server which I didn't notice. This question solved it for me: Decompressing GZip Stream from HTTPClient Response
I find it works well with these different classes WebRequest and HttpWebResponse. I have not added plumbing for resp.StatusCode etc but obviously presuming all went well is a tad naive.
Give it a go i am sure You'll find the WebRequest and HttpWebResponse more capable for dynamic requests (?)
var req = WebRequest.CreateHttp(url)
var getResponse = req.GetResponseAsync();
getResponse.Wait(ResponseTimeoutMilliseconds);
var resp = (HttpWebResponse)getResponse.Result;
using (Stream responseStream = resp.GetResponseStream())
{
var reader = new StreamReader(responseStream, Encoding.UTF8);
string content = reader.ReadToEnd();
}
Obviously once you have things working, you should absolutely use the ..Async versions but for debugging, since we already waited for response it is more convenient to simply step through i find, feel free to not take that middle step :)

How to send File Object to Web api using POST call in Xamarin forms.?

I need to make a POST call from my Xamarin forms app where I need to upload a file object as it is to API using POST Call. Is there any way to make it possible?
if you send file object using Base64 or Byte[] then it will allowed only limited may be upto 2-4 Mb but if you have a larger image than that it will not support.
So, Solution is Post Stream Content Like,
var file = await CrossMedia.Current.PickPhotoAsync(new PickMediaOptions
{
PhotoSize = PhotoSize.Full,
CompressionQuality = 100
});
Create Object of MediaFile like, public MediaFile AttachedImage; and Store file into it so Memory stream will not lost. Like,AttachedImage = file
Post Code on API,
HttpClient httpClient = new HttpClient();
MultipartFormDataContent mt = new MultipartFormDataContent();
AttachedImage.GetStream().Position = 0;
StreamContent imagePart = new StreamContent(AttachedImage.GetStream());
imagePart.Headers.Add("Content-Type", ImageType);
mt.Add(imagePart, String.Format("file"), String.Format("bk.jpeg"));
requestMessage.Content = mt;
var response = await httpClient.PostAsync("Your URL", mt);
if (response.IsSuccessStatusCode)
{
var responseString = await response.Content.ReadAsStringAsync();
var objRootObjectuploadImage = JsonConvert.DeserializeObject<RootObjectuploadImage>(responseString);
if (objRootObjectuploadImage != null)
{
}
else
{
}
}
else
{
Loading(ActIndicator, false);
await DisplayAlert(res.LAlert, "webserver not responding.", res.LOk);
}
NO, it is not possible to send file object. You can send as a json by converting the file in the Base64 string. This is the advised proven solution. This link has code for converting back and forth from Base64.

Error 404 using MS Cognitive services Image Analysis demo code

Using the code from the MS Cognitive services example app at https://learn.microsoft.com/en-us/azure/cognitive-services/computer-vision/quickstarts/csharp I get Error 404 when trying to run the following code with imageFilePath set a local JPEG file:
static async void MakeAnalysisRequest(string imageFilePath)
{
HttpClient client = new HttpClient();
// Request headers.
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", subscriptionKey);
// Request parameters. A third optional parameter is "details".
string requestParameters = "visualFeatures=Description&language=en";
// Assemble the URI for the REST API Call.
string uri = uriBase + "?" + requestParameters;
HttpResponseMessage response;
// Request body. Posts a locally stored JPEG image.
byte[] byteData = GetImageAsByteArray(imageFilePath);
using (ByteArrayContent content = new ByteArrayContent(byteData))
{
// This example uses content type "application/octet-stream".
// The other content types you can use are "application/json" and "multipart/form-data".
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
// Execute the REST API call.
response = await client.PostAsync(uri, content);
// Get the JSON response.
string contentString = await response.Content.ReadAsStringAsync();
// Display the JSON response.
Console.WriteLine("\nResponse:\n");
Console.WriteLine(JsonPrettyPrint(contentString));
}
subscriptionKey is KEY1 from the two keys provided with my Azure subscription (in Eastern Australia location) and uribase is
const string uriBase = "https://australiaeast.api.cognitive.microsoft.com/vision/v1.0";
The combination of KEY1 and uriBase works OK with the demo of the API at
https://australiaeast.dev.cognitive.microsoft.com/docs/services/56f91f2d778daf23d8ec6739/operations/56f91f2e778daf14a499e1fa/console
when the target file is a URL rather than a local file. The image is correctly analyzed.
Why am I getting the 404 error when trying to post from C#? Is it the fact that I am using content type application/octet-stream?
Code for GetImageAsByteArray is:
static byte[] GetImageAsByteArray(string imageFilePath)
{
FileStream fileStream = new FileStream(imageFilePath, FileMode.Open, FileAccess.Read);
BinaryReader binaryReader = new BinaryReader(fileStream);
return binaryReader.ReadBytes((int)fileStream.Length);
}
You're missing the last portion of the request URI. It should be:
string uri = uriBase + "/analyze?" + requestParameters;

415 Unsupported Media Type on POST to .NET Web API

I have a method which POSTs an Image to a web API. However, I am consistently getting HTTP error 415 Unsupported Media Type.
My host method looks like this:
[HttpPost]
[Route("Image/Post")]
public HttpResponseMessage Post(Image image)
{
// do stuff
return Request.CreateResponse(HttpStatusCode.Created);
}
In my calling method, I have code like this:
string url = String.Concat(this.WebApiUrl, "Image/Post");
HttpContent original = new ByteArrayContent(ImageUtility.ImageToByteArray(image));
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(url);
HttpResponseMessage response = client.PostAsync(url, original).Result;
response.EnsureSuccessStatusCode();
For reference, the ImageToByteArray() method:
public static byte[] ImageToByteArray(Image image)
{
if (image == null) {throw new ArgumentNullException("image"); }
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, image.RawFormat);
return ms.ToArray();
}
}
When I call client.PostAsync().Result, I get the 415. Clearly something is missing here, but I haven't been able to connect the dots. Any ideas?
WebApi can't bind a stream of image bytes to an Image instance. It's better to accept that this is binary data and to act accordingly.
IIRC, you can receive the request body something like this:
public async Task<HttpResponseMessage> Post()
{
var requestStream = await Request.Content.ReadAsStreamAsync();
var contentType = Request.Content.Headers.ContentType;
//store content-type and contents of requestStream
return Request.CreateResponse(HttpStatusCode.Created);
}
make sure you set a content-type at the sending end.

How to retrieve the posted data (in byte[] format)?

I have a C# .net web application. I am trying to post a binary data from one application to another using this code
string url = "path to send the data";
string result=null;
string postData = "This is a test that posts this string to a Web server.";
byte[] fileData = Encoding.UTF8.GetBytes (postData);
// Create a request using a URL that can receive a post.
WebRequest request = WebRequest.Create (url);
// Set the Method property of the request to POST.
request.Method = "POST";
// Create POST data and convert it to a byte array.
// Set the ContentType property of the WebRequest.
request.ContentType = "multipart/form-data";
// Set the ContentLength property of the WebRequest.
request.ContentLength = fileData.Length;
// Get the request stream.
Stream dataStream = request.GetRequestStream ();
// Write the data to the request stream.
dataStream.Write (fileData, 0, fileData.Length);
// Close the Stream object.
dataStream.Close ();
// Get the response.
WebResponse response = request.GetResponse();
// Display the status.
result = ((HttpWebResponse)response).StatusDescription;
// Get the stream containing content returned by the server.
dataStream = response.GetResponseStream ();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader (dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd ();
// Display the content.
result = result + responseFromServer;
// Clean up the streams.
reader.Close ();
dataStream.Close ();
response.Close();
By the above code, I am sending byte[] to a second application. How can I retrieve the posted data (in byte[] format) in the second application?
Note: I assume that you are asking about how to retrieve the posted data in second application and also you have access to the code of second application.
Then if it is a webform application then simply on page_load event you can get file name and file itself as:
string strFileName = Request.Files[0].FileName;
HttpPostedFileBase filesToSave = Request.Files[0];
If this is not the requirement, then edit your question and add more details.
EDIT: Updated answer to include both Request and Server side. Server side converts Base64 string to a byte[].
If you're going to post binary data that was read into a byte[], you'll have to convert it to a Base64 string on request side to post it.
Client/Request Side:
byte[] byteData = ReadSomeData();
string postData = Convert.ToBase64String(byteData);
Then on the server side, use the HttpContext to get the InputStream from the Request property. You can then use a StreamReader and its ReadToEnd() method to read in the data. You then convert the posted Base64 string to a byte[].
Something like this:
string postData = string.Empty;
using (StreamReader reader = new StreamReader(context.Request.InputStream))
{
postData = inputStreamReader.ReadToEnd();
}
byte[] data = Convert.FromBase64String(postData);

Categories