C# web api post image as form data - c#

In postman it is easily done:
In order to implement it by web api, i've written below code:
HttpClient client = new HttpClient();
string url = "https://upload.*******.com/api/upload/photos";
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("multipart/form-data"));
byte[] data;
using (var br = new BinaryReader(inputPhotoUpload.files.OpenReadStream()))
data = br.ReadBytes((int) inputPhotoUpload.files.OpenReadStream().Length);
ByteArrayContent bytes = new ByteArrayContent(data);
MultipartFormDataContent multipartFormDataContent = new
MultipartFormDataContent();
multipartFormDataContent.Add(new StringContent(inputPhotoUpload.watermark.ToString()), "watermark");
multipartFormDataContent.Add(new StringContent("listing"), "type");
multipartFormDataContent.Add(bytes, "files", inputPhotoUpload.files.FileName);
HttpResponseMessage res = await client.PostAsync(url, multipartFormDataContent);
my InputPhotoUpload class
public class InputPhotoUpload
{
public string watermark { get; set; }
public string type { get; set; }
public IFormFile files { get; set; }
}
The uploading server accepts and image in byte array and two other values named listing and watermark. the server gives code 500 internal error.
My goal is to post a form-data post to upload an image, post man is able to do it yet my controller api cannot.

apparently i needed to sync all headers with postman, some were unnecessary.
HttpClient client = new HttpClient();
MultipartFormDataContent form = new MultipartFormDataContent();
client.DefaultRequestHeaders.Clear();
ByteArrayContent bytes = new ByteArrayContent(imageStream);
form.Add(bytes, "files", imageName);
form.Add(new StringContent("En"), "watermark");
form.Add(new StringContent("listing"), "type");
HttpResponseMessage response = await client.PostAsync("https://upload.*****.com/api/upload/photos", form);
var k = response.Content.ReadAsStringAsync().Result;
_Result = JsonConvert.DeserializeObject<List<Output>>(k);

Related

How to split an HTTP Request Body in two parts?

I'm fairly new to use HTTPClient and sending REST requests to APIs, I'm currently practicing multipart upload using this Google Drive API endpoint:
POST https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart
There's an instruction that states there to split the request body into two parts, I tried to recreate this but was unable to do so.
https://developers.google.com/drive/api/guides/manage-uploads#multipart
Here's my current code:
async void UploadFile(StorageFile fileName)
{
using (HttpClient client = new HttpClient())
{
// Opens files and convert it to stream
var resultStream = await fileName.OpenReadAsync();
var fileStreamContent = new StreamContent(resultStream.AsStream());
// Create file MetaData
var fileMetaData = JsonConvert.SerializeObject(
new { name = fileName.Name, mimetype = fileName.ContentType });
// Create POST request
var requestMessage = new HttpRequestMessage(HttpMethod.Post, uploadFileEndpoint);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(tokenType, accessToken);
// Add request body
requestMessage.Content = new StringContent(fileMetaData, Encoding.UTF8, "application/json");
requestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("multipart/related");
var response = await client.SendAsync(requestMessage);
string responseString = await response.Content.ReadAsStringAsync();
output(responseString);
}
}
Any help would be greatly appreciated, thank you!
According to the documentation on Perform a multipart upload (HTTP tab), you need the MultipartFormDataContent as suggested by #Jeremy.
There are a few things needed to perform/migrate:
Add AuthenticationHeaderValue into client.DefaultRequestHeaders.Authorization.
Create a StreamContent instance, fileStreamContent (which you have done) and specify its Headers.ContentType.
Create a StringContent instance, stringContent (which you have done).
Append both StreamContent and StringContent into the MultipartFormDataContent instance, formData.
Specify the formData's Headers.ContentType as requested in API docs.
Post the formData with await client.PostAsync(/* API Url */, formData);
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(tokenType, accessToken);
// Opens files and convert it to stream
var resultStream = await fileName.OpenReadAsync();
var fileStreamContent = new StreamContent(resultStream.AsStream());
fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue(fileName.ContentType);
// Create file MetaData
var fileMetaData = JsonConvert.SerializeObject(new { name = fileName.Name, mimetype = fileName.ContentType });
var stringContent = new StringContent(fileMetaData, Encoding.UTF8, "application/json");
// Create POST request
MultipartFormDataContent formData = new MultipartFormDataContent();
formData.Add(stringContent, "metadata");
formData.Add(fileStreamContent, "media");
formData.Headers.ContentType = new MediaTypeHeaderValue("multipart/related");
var response = await client.PostAsync(uploadFileEndpoint, formData);
string responseString = await response.Content.ReadAsStringAsync();
}

Posting IFormFile to webAPI from c#

I've got a Web API method which accepts a list of IFormFile variables within a small class structure, to upload the files to a storage account.
public class FileInputModel
{
public int ScenarioId { get; set; }
public IList<IFormFile> UploadFiles { get; set; }
}
[HttpPost("UploadFiles")]
public async Task<IActionResult> UploadForm([FromForm] FileInputModel files)
{
//UploadLogic
}
This works perfectly for a https post using Postman, but i can't quite seem to figure out how to do this using a C# programme i'm writing to link directly to this api. So far I've got some code to convert a FileStreamResult variable into an IformFile to then send in a post request, but i can't figure out how to get a FileStreamResult from a file on my pc. Here's the method i have so far.
var json = JsonSerializer.Serialize(FileInputModel);
StringContent data = new StringContent(json, Encoding.UTF8, "application/json");
try
{
using HttpClient client = new HttpClient();
HttpResponseMessage response = await client.PostAsync(url, data);
return response;
}
I was getting too caught up on the IFormFile aspect of the backend, when in reality the function was just opening the stream to then use in further functions. With this I solved it by simply using a filestream in the frontend connecting c# programme and sending the information as a MultipartFormDataContent type.
using (var client = new HttpClient())
{
using (var content = new MultipartFormDataContent())
{
var fileName = Path.GetFileName(filePath);
var fileStream = System.IO.File.Open(filePath, FileMode.Open);
content.Add(new StreamContent(fileStream), "file", fileName);
var requestUri = baseURL;
var request = new HttpRequestMessage(HttpMethod.Post, requestUri) { Content = content };
var result = await client.SendAsync(request);
return;
}
}

Send an form-data post request with .Net framework HttpClient class containing a file

I need to recreate this request I made in Postman with C#, I found that the HttpClient class solves most of my problems, but this time I couldn't solve it on my own.
I embbeded an image with an example of the very post request.
POST REQUEST IN POSTMAN
There are three text paramethers and one file I need to send, with a content-type of form-data, the file needs to be a .json.
I tried constructing the POST request in many ways; this is my last version:
string endpoint = $"{Endpoint}/captcha";
string token_paramsJSON = JsonConvert.SerializeObject(v3Request.token_params);
Hashtable ParametrosPOSTCaptcha = GetV3POSTParams(v3Request);
UnicodeEncoding uniEncoding = new UnicodeEncoding();
using (Stream ms = new MemoryStream()) {
var sw = new StreamWriter(ms, uniEncoding);
sw.Write(token_paramsJSON);
sw.Flush();
ms.Seek(0, SeekOrigin.Begin);
using (MultipartFormDataContent form = new MultipartFormDataContent())
{
form.Add(new StringContent(v3Request.username), "username");
form.Add(new StringContent(v3Request.password), "password");
form.Add(new StringContent(v3Request.type.ToString()), "type");
form.Add(new StreamContent(ms));
var response = await _httpClient.PostAsync(endpoint, form);
string ResponseTest = await GetResponseText(response);
}
}
With this code, I successfully establish a connection with the endpoint, send the username and password.
But the response differs from the one I get with Postman using the same paramethers:
Postman: x=0&xx=1892036372&xxx=&xxxxx=1
The actual response I get is this:
HttpClient: {"error": "not-logged-in"}
Thanks in advance!
Finally, I could solve it using the following implementation:
string endpoint = $"{Endpoint}/endpointName";
string token_paramsJSON = JsonConvert.SerializeObject(v3Request.token_params, Formatting.Indented);
Dictionary<string,string> PostParams = GetPOSTParams(v3Request);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, endpoint);
UnicodeEncoding uniEncoding = new UnicodeEncoding();
using (MultipartFormDataContent form = new MultipartFormDataContent())
{
foreach(var field in PostParams)
{
StringContent content = new StringContent(field.Value);
content.Headers.ContentType = null;
form.Add(content, field.Key);
}
var JsonFile = new StringContent(token_paramsJSON);
JsonFile.Headers.ContentType = new MediaTypeHeaderValue("application/json");
JsonFile.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = "\"token_params\"",
FileName = "\"token.json\""
};
form.Add(JsonFile);
request.Content = form;
var response = await _httpClient.SendAsync(request, CancellationToken.None);
return await GetCaptchaFromResponse(response);
}

How to send ProtoBuf as bytes in HtttpClient? [duplicate]

I want to post this data to Web API server:
public sealed class SomePostRequest
{
public int Id { get; set; }
public byte[] Content { get; set; }
}
Using this code for server:
[Route("Incoming")]
[ValidateModel]
public async Task<IHttpActionResult> PostIncomingData(SomePostRequest requestData)
{
// POST logic here
}
and this - for client:
var client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:25001/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "id", "1" },
{ "content", "123" }
});
var result = await client.PostAsync("api/SomeData/Incoming", content);
result.EnsureSuccessStatusCode();
everything works fine (at least, debugger stops at breakpoint in PostIncomingData).
Since there is a byte array, I don't want to serialize it as JSON, and want to post it as binary data to decrease network traffic (something like application/octet-stream).
How this can be achieved?
I've tried to play with MultipartFormDataContent, but looks like I just can't understand, how MultipartFormDataContent will match signature of controller's method.
E.g., replacing content to this:
var content = new MultipartFormDataContent();
content.Add(new FormUrlEncodedContent(new Dictionary<string, string> { { "id", "1" } }));
var binaryContent = new ByteArrayContent(new byte[] { 1, 2, 3 });
binaryContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
content.Add(binaryContent, "content");
var result = await client.PostAsync("api/SomeData/Incoming", content);
result.EnsureSuccessStatusCode();
leads to error 415 ("Unsupported media type").
WebAPI v2.1 and beyond supports BSON (Binary JSON) out of the box, and even has a MediaTypeFormatter included for it. This means you can post your entire message in binary format.
If you want to use it, you'll need to set it in WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Formatters.Add(new BsonMediaTypeFormatter());
}
}
Now, you an use the same BsonMediaTypeFormatter at the client side to serialize your request:
public async Task SendRequestAsync()
{
var client = new HttpClient
{
BaseAddress = new Uri("http://www.yourserviceaddress.com");
};
// Set the Accept header for BSON.
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/bson"));
var request = new SomePostRequest
{
Id = 20,
Content = new byte[] { 2, 5, 7, 10 }
};
// POST using the BSON formatter.
MediaTypeFormatter bsonFormatter = new BsonMediaTypeFormatter();
var result = await client.PostAsync("api/SomeData/Incoming", request, bsonFormatter);
result.EnsureSuccessStatusCode();
}
Or, you can use Json.NET to serialize your class to BSON. Then, specify you want to use "application/bson" as your "Content-Type":
public async Task SendRequestAsync()
{
using (var stream = new MemoryStream())
using (var bson = new BsonWriter(stream))
{
var jsonSerializer = new JsonSerializer();
var request = new SomePostRequest
{
Id = 20,
Content = new byte[] { 2, 5, 7, 10 }
};
jsonSerializer.Serialize(bson, request);
var client = new HttpClient
{
BaseAddress = new Uri("http://www.yourservicelocation.com")
};
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/bson"));
var byteArrayContent = new ByteArrayContent(stream.ToArray());
byteArrayContent.Headers.ContentType = new MediaTypeHeaderValue("application/bson");
var result = await client.PostAsync(
"api/SomeData/Incoming", byteArrayContent);
result.EnsureSuccessStatusCode();
}
}
I convert Byte Array into Base64 String to post:
await client.PostAsJsonAsync( apiUrl,
new {
message = "",
content = Convert.ToBase64String(yourByteArray),
}
);
and receiver can convert the Base64 String back to Byte Array by:
string base64Str = (string)postBody.content;
byte[] fileBytes = Convert.FromBase64String(base64Str);
I have created this generic and cross platform method to support the BSON format using the Json.NET library so we can reuse it easier later. It works fine in Xamarin platform as well.
public static async HttpResponseMessage PostBsonAsync<T>(string url, T data)
{
using (var client = new HttpClient())
{
//Specifiy 'Accept' header As BSON: to ask server to return data as BSON format
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/bson"));
//Specify 'Content-Type' header: to tell server which format of the data will be posted
//Post data will be as Bson format
var bSonData = HttpExtensions.SerializeBson<T>(data);
var byteArrayContent = new ByteArrayContent(bSonData);
byteArrayContent.Headers.ContentType = new MediaTypeHeaderValue("application/bson");
var response = await client.PostAsync(url, byteArrayContent);
response.EnsureSuccessStatusCode();
return response;
}
}
The method to help to serialise data to BSON format:
public static byte[] SerializeBson<T>(T obj)
{
using (MemoryStream ms = new MemoryStream())
{
using (BsonWriter writer = new BsonWriter(ms))
{
JsonSerializer serializer = new JsonSerializer();
serializer.Serialize(writer, obj);
}
return ms.ToArray();
}
}
Then you can use the Post method like this:
var response = await PostBsonAsync<SamplePostRequest>("api/SomeData/Incoming", requestData);
Fyi, for protobuf serialization to request body posts
LoginRequest loginRequest = new LoginRequest()
{
Code = "UserId",
Password = "myPass",
CMToken = "eIFt4lYTKGU:APA91bFZPe3XCDL2r1JUJuEQLlN3FoeFw9ULpw8ljEavNdo9Lc_-Qua4w9pTqdOFLTb92Kf03vyWBqkcvbBfYEno4NQIvp21kN9sldDt40eUOdy0NgMRXf2Asjp6FhOD1Kmubx1Hq7pc",
};
byte[] rawBytes = ProtoBufSerializer.ProtoSerialize<LoginRequest>(loginRequest);
var client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:9000/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/x-protobuf"));
//var bSonData = HttpExtensions.SerializeBson<T>(data);
var byteArrayContent = new ByteArrayContent(rawBytes);
byteArrayContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-protobuf");
var result = client.PostAsync("Api/Login", byteArrayContent).Result;
Console.WriteLine(result.IsSuccessStatusCode);
I wanted to send it truly binary like I did with WebClient before not make it multipart.
Using inspiration from this question I got it working this way:
HttpClient InternalHttpClient = new HttpClient();
HttpContent BinaryContent = new ByteArrayContent(new byte[] { 1, 2, 3 });
byte[] ReceivedData = new byte[0];
using (HttpResponseMessage ResponseMessage = InternalHttpClient.PostAsync("apiurl/binarycomms.aspx", BinaryContent).Result)
{
using (HttpContent ResponseBytes = ResponseMessage.Content)
{
ReceivedData = ResponseBytes.ReadAsByteArrayAsync().Result;
}
}
On the server side the code is also fully binary:
protected void Page_Load(object sender, EventArgs e)
{
Page.Response.ContentType = "application/octet-stream";
byte[] Challenge = Page.Request.BinaryRead(Request.TotalBytes);
Page.Response.BinaryWrite(new byte[] { 10, 20, 30 });
}
You can easily add compression to this communication to make the bandwidth usage even smaller.
Love to hear comments should I have missed something or if this is off topic, but it works like a charm for me.

Upload image and string via HTTP POST windows phone 8.1 with HttpClient

I have a Windows Phone application in C#. I am trying send a image (byte[]) and a session token (string) to my django server, but not how to do it.
I 've looked at other post but it does not work what they do , or classes that use do not exist.
The header of my function is:
public static async Task<bool> sendImagePerfil(string token, byte[] imagen)
{
using (var client = new HttpClient())
{
var values = new List<KeyValuePair<string, string>>();
values.Add(new KeyValuePair<string, string>("token", token));
values.Add(new KeyValuePair<string, string>("image", Convert.ToString(imagen)));
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("MyURL.domain/function", content);
var responseString = await response.Content.ReadAsStringAsync();
}
}
EDITED: My problem now is my server don't get the image. The django code is:
if request.method == 'POST':
form = RestrictedFileField(request.POST, request.FILES)
token = models.UsuarioHasToken.objects.get(token=parameters['token'])
user = token.user
print (request.FILES['image'])
user.image = request.FILES['image']
I can't modify the django code because this code it's working with Android app
Using this response ,
How to upload file to server with HTTP POST multipart/form-data
Try with this...
HttpClient httpClient = new HttpClient();
MultipartFormDataContent form = new MultipartFormDataContent();
form.Add(new StringContent(token), "token");
var imageForm = new ByteArrayContent(imagen, 0, imagen.Count());
imagenForm.Headers.ContentType = new MediaTypeHeaderValue("image/jpg");
form.Add(imagenForm, "image", "nameholder.jpg");
HttpResponseMessage response = await httpClient.PostAsync("your_url_here", form);
response.EnsureSuccessStatusCode();
httpClient.Dispose();
string result = response.Content.ReadAsStringAsync().Result;

Categories

Resources