How to post image as byte array in visual studio webtest - c#

I'm trying to test my web api but I can't post image as byte array. How can I do it? I'm using File Upload Parameter and specify content-type application/octet-stream but I'm getting 415 unsupported media type. How can I post image as byte array ?
Here is my request:
And Form Post Parameter Properties:
Here is my web api post method:
Here is my webtest request log:
Edit:
#nick_w has great answer but I found another way. I generate code from my webtest file. Correct test request should be shown as below:
WebTestRequest request2 = new WebTestRequest("http://url/api/SendStream");
request2.Method = "POST";
request2.Headers.Add(new WebTestRequestHeader("Content-Type", "application/octet-stream"));
request2.QueryStringParameters.Add("JobId", this.Context["JobId"].ToString(), false, false);
FileStream oFileStream = new FileStream("4_e.jpg", FileMode.Open, FileAccess.Read);
byte[] bytes = new byte[oFileStream.Length];
oFileStream.Read(bytes, 0, System.Convert.ToInt32(oFileStream.Length));
oFileStream.Close();
BinaryHttpBody request2Body = new BinaryHttpBody();
request2Body.ContentType = "application/octet-stream";
request2Body.Data = bytes;
request2.Body = request2Body;
request2.SendChunked = true;
request2.Timeout = 10000000;
yield return request2;
request2 = null;

I expect you are having issues binding the stream parameter to a byte array. Based on information found here and in this question, try changing your method to something like:
public async Task<ResponseEntity<SendStreamResponse>> Post([FromUri]int jobId)
{
byte[] stream = await Request.Content.ReadAsByteArrayAsync();
return await SendStreamAsync(jobId, stream);
}

Related

Damaged png as result of attempt toupload image to server using MultipartFormDataContent

I am trying to upload png from my Xamarin iOS app. Server returns code 200 and a link of that image. When I try to copy-paste this link to browser, it shows damaged image template.
When I try this steps using postman, everything is fine. Server gives me the working link. And I can see my image in browser after copy-paste.
I alredy compared headers from postman many times. Nothing helped. Also I checked if the byte array is valid from my app. Yes it is valid. I have made image from it and displayed it on a mobile.
My request:
client.DefaultRequestHeaders.Add("Accept", "*/*");
client.DefaultRequestHeaders.Add("ContentType", "multipart/form-data");
client.DefaultRequestHeaders.Add("Connection", "keep-alive");
var imageContent = new ByteArrayContent(imgBytes, 0, imgBytes.Length);
imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse("image/png");
using (var formData = new MultipartFormDataContent())
{
formData.Add(imageContent, "pic", "picture.png");
var result = await client.PostAsync($"{Constants.MainUrl}{"/api/Profile/EditProfilePic"}", formData);
var resContent =await result.Content.ReadAsStringAsync();
return result;
}
My byte converting:
using (NSData imageData = _currentImg.AsPNG())
{
Byte[] imgByteArray = new Byte[imageData.Length];
Needed this code while converting image to byte array. Exactly System.Runtime.InteropServices...
Full code below:
Byte[] imgByteArray = new Byte[imageData.Length]; System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, imgByteArray, 0, Convert.ToInt32(imageData.Length));

How to handle png from rest api?

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);
}

The remote server returned an error: (415) Unsupported Media Type

I try to upload a text file from WPF RESTful client to ASP .NET MVC WebAPI 2 website.
Client code
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("http://localhost:22678/api/Account/UploadFile?fileName=test.txt&description=MyDesc1");
request.Method = WebRequestMethods.Http.Post;
request.Headers.Add("Authorization", "Bearer " + tokenModel.ExternalAccessToken);
request.ContentType = "text/plain";
request.MediaType = "text/plain";
byte[] fileToSend = File.ReadAllBytes(#"E:\test.txt");
request.ContentLength = fileToSend.Length;
using (Stream requestStream = request.GetRequestStream())
{
// Send the file as body request.
requestStream.Write(fileToSend, 0, fileToSend.Length);
requestStream.Close();
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
Console.WriteLine("HTTP/{0} {1} {2}", response.ProtocolVersion, (int)response.StatusCode, response.StatusDescription);
WebAPI 2 code
[HttpPost]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("UploadFile")]
public void UploadFile(string fileName, string description, Stream fileContents)
{
byte[] buffer = new byte[32768];
MemoryStream ms = new MemoryStream();
int bytesRead, totalBytesRead = 0;
do
{
bytesRead = fileContents.Read(buffer, 0, buffer.Length);
totalBytesRead += bytesRead;
ms.Write(buffer, 0, bytesRead);
} while (bytesRead > 0);
var data = ms.ToArray() ;
ms.Close();
Debug.WriteLine("Uploaded file {0} with {1} bytes", fileName, totalBytesRead);
}
So.. Under the client code I am facing that exception
The remote server returned an error: (415) Unsupported Media Type.
Any clue what do I am missing?
You are setting the ContentType = "text/plain", and this settings drive the Formatter selection. Please check Media Formatters for more details.
An extract:
In Web API, the media type determines how Web API serializes and
deserializes the HTTP message body. There is built-in support for XML,
JSON, and form-urlencoded data, and you can support additional media
types by writing a media formatter.
So, there is no built in text/plain formatter, ie: Unsupported Media Type. You can change the content-type to some supported, built-in, or implement custome one (as described in the link)
+1 on what Radim has mentioned above...As per your action Web API model binding notices that the parameter fileContents is a complex type and by default assumes to read the request body content using the formatters. (note that since fileName and description parameters are of string type, they are expected to come from uri by default).
You can do something like the following to prevent model binding to take place:
[HttpPost]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("UploadFile")]
public async Task UploadFile(string fileName, string description)
{
byte[] fileContents = await Request.Content.ReadAsByteArrayAsync();
....
}
BTW, what do you plan to do with this fileContents? are you trying to create a local file? if yes, there is a better way to handle this.
Update based on your last comment:
A quick example of what you could do
[HttpPost]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("UploadFile")]
public async Task UploadFile(string fileName, string description)
{
Stream requestStream = await Request.Content.ReadAsStreamAsync();
//TODO: Following are some cases you might need to handle
//1. if there is already a file with the same name in the folder
//2. by default, request content is buffered and so if large files are uploaded
// then the request buffer policy needs to be changed to be non-buffered to imporve memory usage
//3. if exception happens while copying contents to a file
using(FileStream fileStream = File.Create(#"C:\UploadedFiles\" + fileName))
{
await requestStream.CopyToAsync(fileStream);
}
// you need not close the request stream as Web API would take care of it
}

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);

Anyone have sample code for doing a "chunked" HTTP streaming download of one web directly to a upload to a separate web server?

Background - I'm trying to stream an existing webpage to a separate web application, using HttpWebRequest/HttpWebResponse in C#. One issue I'm striking is that I'm trying to set the file upload request content-length using the file download's content-length, HOWEVER the issue seems to be when the source webpage is on a webserver for which the HttpWebResponse doesn't provide a content length.
HttpWebRequest downloadRequest = WebRequest.Create(new Uri("downloaduri")) as HttpWebRequest;
using (HttpWebResponse downloadResponse = downloadRequest.GetResponse() as HttpWebResponse)
{
var uploadRequest = (HttpWebRequest) WebRequest.Create(new Uri("uripath"));
uploadRequest.Method = "POST";
uploadRequest.ContentLength = downloadResponse.ContentLength; // ####
QUESTION : How could I update this approach to cater for this case (when the download response doesn't have a content-length set). Would it be to somehow use a MemoryStream perhaps? Any sample code would be appreciated. In particular is there a code sample someone would have that shows how to do a "chunked" HTTP download & upload to avoid any issues of the source web server not providing content-length?
Thanks
As I already applied in the Microsoft Forums, there are a couple of options that you have.
However, this is how I would do it with a MemoryStream:
HttpWebRequest downloadRequest = WebRequest.Create(new Uri("downloaduri")) as HttpWebRequest;
byte [] buffer = new byte[4096];
using (MemoryStream ms = new MemoryStream())
using (HttpWebResponse downloadResponse = downloadRequest.GetResponse() as HttpWebResponse)
{
Stream respStream = downloadResponse.GetResponseStream();
int read = respStream.Read(buffer, 0, buffer.Length);
while(read > 0)
{
ms.Write(buffer, 0, read);
read = respStream.Read(buffer, 0, buffer.Length);
}
// get the data of the stream
byte [] uploadData = ms.ToArray();
var uploadRequest = (HttpWebRequest) WebRequest.Create(new Uri("uripath"));
uploadRequest.Method = "POST";
uploadRequest.ContentLength = uploadData.Length;
// you know what to do after this....
}
Also, note that you really don't need to worry about knowing the value for ContentLength a priori. As you have guessed, you could have set SendChunked to true on uploadRequest, and then just copied from the download stream into the upload stream. Or, you can just do the copy without setting chunked, and HttpWebRequest (as far as I know) will buffer the data internally (make sure AllowWriteStreamBuffering is set to true on uploadrequest) and figure out the content length and send the request.

Categories