Send image with HttpClient to POST WebApi and consume the data - c#

My HttpClient sends the image with PostAsync.
I am not really sure what to do now since this is my first REST Api and I can't really adapt the things I sorted out in other posts yet.
Hopefully you guys can help me out and can give me a direction.
public async Task SendImage(string fullFileName, string fileName)
{
var client = new HttpClient();
client.BaseAddress = new Uri("http://x.x.x.x");
var content = new StringContent(fullFileName, Encoding.UTF8, "image/jpg");
HttpResponseMessage response = await client.PostAsync($"/values/file/{fileName}", content);
}
I have several questions about the POST function.
First of all I can successfully access it with PostMan and the fileName is correct.
How do I read the image data and write it to a file?
[HttpPost("file/{fileName}")]
public void Upload(string fileName)
{
Debug.Write(fileName);
}
EDIT:
I setup my environment and I can now send a post via the internet to my published Web Api.
On my app just nothing happens.
For now I just tried to get something of a message to work on but I dont getting one.
[HttpPost("file/{fileName}")]
public HttpResponseMessage Upload(UploadedFile fileName)
{
Debug.Write(fileName);
if (!ModelState.IsValid)
{
}
if (fileName == null)
{
}
string destinationPath = Path.Combine(#"C:\", fileName.FileFullName);
System.IO.File.WriteAllBytes(destinationPath, fileName.Data);
HttpResponseMessage rm = new HttpResponseMessage();
rm.StatusCode = HttpStatusCode.OK;
return rm;
}

1.Your controller should look like this:
//For .net core 2.1
[HttpPost]
public IActionResult Index(List<IFormFile> files)
{
//Do something with the files here.
return Ok();
}
//For previous versions
[HttpPost]
public IActionResult Index()
{
var files = Request.Form.Files;
//Do something with the files here.
return Ok();
}
2.To upload a file you can also use Multipart content:
public async Task UploadImageAsync(Stream image, string fileName)
{
HttpContent fileStreamContent = new StreamContent(image);
fileStreamContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data") { Name = "file", FileName = fileName };
fileStreamContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
using (var client = new HttpClient())
using (var formData = new MultipartFormDataContent())
{
formData.Add(fileStreamContent);
var response = await client.PostAsync(url, formData);
return response.IsSuccessStatusCode;
}
}
3.If you are uploading large files you should consider streaming the files instead, you can read about it here

You're going fine until you're trying to retrieve the image data, I'm afraid.
According to your question:
How do I read the image data and write it to a file?
All you want to do is getting the file's data and its file name and sending it to your service.
I would personally create an UploadedFile class on both sides (client and service side), having the file's name and its data, so:
public class UploadedFile
{
public string FileFullName { get; set; }
public byte[] Data { get; set; }
public UploadedFile(string filePath)
{
FileFullName = Path.GetFileName(Normalize(filePath));
Data = File.ReadAllBytes(filePath);
}
private string Normalize(string input)
{
return new string(input
.Normalize(System.Text.NormalizationForm.FormD)
.Replace(" ", string.Empty)
.ToCharArray()
.Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
.ToArray());
}
}
Then you will need, for example, the NewtonSoft's JsonConvert in order to serialize the object and send it through.
So now you would be able to send your data async:
public async Task SendDataAsync(string fullFilePath)
{
if (!File.Exists(fullFilePath))
throw new FileNotFoundException();
var data = JsonConvert.SerializeObject(new UploadedFile(fullFilePath));
using (var client = new WebClient())
{
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
await client.UploadStringTaskAsync(new Uri("http://localhost:64204/api/upload/"), "POST", data);
}
}
Now, make sure you correctly handle the request on the server side. If for whatever reason the parameters doesn't match it won't enter into the method (remember having the same model/class - UploadedFile on the service as well).
On the service, just change the arguments of your method and perform something "like this":
[HttpPost]
public HttpResponseMessage Upload(UploadedFile file)
{
if (!ModelState.IsValid)
...
if (file == null)
...
string destinationPath = Path.Combine(_whateverPath, file.FileFullName);
File.WriteAllBytes(destinationPath, file.Data);
}
Hope it helped you having an idea about what to do and what you're actually doing wrong. I've exposed something similar based in my experience.
EDIT: I've actually uploaded an example with both sides working: a simple .NET Core console app which retrieves a file and sends it through POST and a basic WebAPI2 service with a simple controller to retrieve the data. Both ready to go and tested working! Download it here.
Enjoy.

Related

In ASP.NET 4.8, how can I deserilize a file containing JSON, uploaded as POST multipart/form-data request to an object in memory (no file on server)?

Example code:
I export an object as JSON in a file
[HttpGet]
public IHttpActionResult ExportObjectToFile()
{
var exampleObject = new ExampleObject();
var response = Request.CreateResponse(HttpStatusCode.OK, exampleObject, JsonMediaTypeFormatter.DefaultMediaType);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue(DispositionTypeNames.Attachment)
{
FileName = exampleObject.FileName;
};
return ResponseMessage(response);
}
Now I want to import and create a new object using multipart/form-data on Postman to attach the file with the request
[HttpPost]
public async Task<IHttpActionResult> ImportObjectFromFile()
{
var multipartForm = await Request.Content.ReadAsMultipartAsync().ConfigureAwait(false);
var jsonString = await multipartForm.Contents[0].ReadAsString().ConfigureAwait(false);
var importedExampleObject = JsonConvert.DeserializeObject<exampleObject>(jsonString);
return ResponseMessage(importedExampleObject);
}
I know it's considered bad practice to not store the file first locally but this is more of a learning exercise for me, it helps me understand how file uploads work.
So with the above code importedExampleObject is created but all the fields are null, essentially all the data is lost, and there are no exceptions thrown, so I'm at a loss.

Define header for upload file in API

I am trying to upload excel file to convert it to Json, but i need to passing through API Gateway. I have problem to passing the file from API Gateway.
I try to set header in ContentDisposition, ContentLength and ContentType manually.
using (var client = new HttpClient())
{
using (var Content = new MultipartFormDataContent())
{
var name = Path.GetFileName(postedFile.FileName);
HttpContent content = new StringContent("");
content.Headers.Clear();
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = name,
FileName = name
};
content.Headers.Add("Content-Length", postedFile.ContentLength.ToString());
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("multipart/form-data");
Content.Add(content);
}
HttpResponseMessage reply = new HttpResponseMessage();
reply = await client.GetAsync(#"http://localhost:60897/api/ExceltoJSONConversion");
if (reply.IsSuccessStatusCode)
{
var responseString = await reply.Content.ReadAsStringAsync();
return Json(JsonConvert.DeserializeObject(responseString));
}
}
I have been tried several code but reply always return code 405 MethodNotAllowed.
here my controller where i proceed file
[HttpPost]
[Route("api/ExceltoJSONConversion")]
public IHttpActionResult ExceltoJSONConversion()
{
// Process the file from API Gateway
}
Am i missing something when define Header multipart/form-data? or my code just a mess?
Your API method accepts POST requests only ([HttpPost] attribute).
And in your client you are trying to get API through GET method (client.GetAsync ... ).
Either decorate your API method with [HttpGet] instead of [HttpPost], either change client part to use POST (client.PostAsync ... ).

How to pass form-data and json in same API in Xamarin?

I have an API method in Xamarin that uses .NET Core as a back end and I want to use it to transfer both form-data and JSON format.I have read some examples and I tried to figure a method to make it work.Currently I have this:
public async Task AddUser(User user, MediaFile file)
{
string userRegisterUrl = "http://10.0.2.2:53547/api/PostUser";
var json = JsonConvert.SerializeObject(user);
HttpClientHandler clientHandler = new HttpClientHandler();
clientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
var formData = new MultipartFormDataContent();
formData.Add(new StreamContent(file.GetStream()),"file",string.Format("{0}.png",Guid.NewGuid()));
formData.Add(new StringContent(json),"user");
using (HttpClient client = new HttpClient(clientHandler))
{
var response = await client.PostAsync(userRegisterUrl, formData);
string result = await response.Content.ReadAsStringAsync();
if(response.IsSuccessStatusCode)
{
}
}
}
The problem is that no matter what I do I keep getting this error:"415 Unsupported Media Type".Now I know that what it's trying to say by this is that the server will not accept the payload format as it's unsupported but I have no idea how to do it in such way that it will not require me to install external nuget packages to achieve it.I know it is possible and what I have tried is the following:
First I have tried to create an HttpContent that will get the media file as stream and I added some headers to it with ContentDispositionHeaderValue of "form-data" and a MediaTypeHeaderValue of "application/octet-stream".The json format I would serialize it as I did in my method and pass it to the MultipartFormDataContent value but without any success as I got the error mentioned above.
The second try I didn't pass any headers to the media file,the json object remained unchanged and I used a try catch block to pass the content but without any luck as it gave me the same error.
The method above is the final method that I have implemented and this is what I have on the server-side:
[HttpPost]
public async Task<ActionResult<User>> PostUser(User user,IFormFile file)
{
if (string.IsNullOrWhiteSpace(_env.WebRootPath))
{
_env.WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
}
if(user!=null)
{
_context.User.Add(user);
var users = Path.Combine(_env.WebRootPath, "uploads", user.Name);
if (!Directory.Exists(users)) Directory.CreateDirectory(users);
if (file.Length > 0)
{
using (var fileStream = new FileStream(Path.Combine(users, file.FileName), FileMode.Create))
{
await file.CopyToAsync(fileStream);
}
}
}
await _context.SaveChangesAsync();
return CreatedAtAction("GetUser", new { id = user.IdUser }, user);
}
Initially I had both methods separated,one for uploading the image and the other for storing user data but I want to create a folder with the user's name every time a new user is registered and someone suggested I should merge the two of them.How can I achieve the desired result without having to deal with the unsupported format?
Alternatif solution you can use Refit to simplify your request. It can be used in any xamarin forms project with/withour ReactiveUI

How to Send/Read Image From HttpResponseMessage [duplicate]

My HttpClient sends the image with PostAsync.
I am not really sure what to do now since this is my first REST Api and I can't really adapt the things I sorted out in other posts yet.
Hopefully you guys can help me out and can give me a direction.
public async Task SendImage(string fullFileName, string fileName)
{
var client = new HttpClient();
client.BaseAddress = new Uri("http://x.x.x.x");
var content = new StringContent(fullFileName, Encoding.UTF8, "image/jpg");
HttpResponseMessage response = await client.PostAsync($"/values/file/{fileName}", content);
}
I have several questions about the POST function.
First of all I can successfully access it with PostMan and the fileName is correct.
How do I read the image data and write it to a file?
[HttpPost("file/{fileName}")]
public void Upload(string fileName)
{
Debug.Write(fileName);
}
EDIT:
I setup my environment and I can now send a post via the internet to my published Web Api.
On my app just nothing happens.
For now I just tried to get something of a message to work on but I dont getting one.
[HttpPost("file/{fileName}")]
public HttpResponseMessage Upload(UploadedFile fileName)
{
Debug.Write(fileName);
if (!ModelState.IsValid)
{
}
if (fileName == null)
{
}
string destinationPath = Path.Combine(#"C:\", fileName.FileFullName);
System.IO.File.WriteAllBytes(destinationPath, fileName.Data);
HttpResponseMessage rm = new HttpResponseMessage();
rm.StatusCode = HttpStatusCode.OK;
return rm;
}
1.Your controller should look like this:
//For .net core 2.1
[HttpPost]
public IActionResult Index(List<IFormFile> files)
{
//Do something with the files here.
return Ok();
}
//For previous versions
[HttpPost]
public IActionResult Index()
{
var files = Request.Form.Files;
//Do something with the files here.
return Ok();
}
2.To upload a file you can also use Multipart content:
public async Task UploadImageAsync(Stream image, string fileName)
{
HttpContent fileStreamContent = new StreamContent(image);
fileStreamContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data") { Name = "file", FileName = fileName };
fileStreamContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
using (var client = new HttpClient())
using (var formData = new MultipartFormDataContent())
{
formData.Add(fileStreamContent);
var response = await client.PostAsync(url, formData);
return response.IsSuccessStatusCode;
}
}
3.If you are uploading large files you should consider streaming the files instead, you can read about it here
You're going fine until you're trying to retrieve the image data, I'm afraid.
According to your question:
How do I read the image data and write it to a file?
All you want to do is getting the file's data and its file name and sending it to your service.
I would personally create an UploadedFile class on both sides (client and service side), having the file's name and its data, so:
public class UploadedFile
{
public string FileFullName { get; set; }
public byte[] Data { get; set; }
public UploadedFile(string filePath)
{
FileFullName = Path.GetFileName(Normalize(filePath));
Data = File.ReadAllBytes(filePath);
}
private string Normalize(string input)
{
return new string(input
.Normalize(System.Text.NormalizationForm.FormD)
.Replace(" ", string.Empty)
.ToCharArray()
.Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
.ToArray());
}
}
Then you will need, for example, the NewtonSoft's JsonConvert in order to serialize the object and send it through.
So now you would be able to send your data async:
public async Task SendDataAsync(string fullFilePath)
{
if (!File.Exists(fullFilePath))
throw new FileNotFoundException();
var data = JsonConvert.SerializeObject(new UploadedFile(fullFilePath));
using (var client = new WebClient())
{
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
await client.UploadStringTaskAsync(new Uri("http://localhost:64204/api/upload/"), "POST", data);
}
}
Now, make sure you correctly handle the request on the server side. If for whatever reason the parameters doesn't match it won't enter into the method (remember having the same model/class - UploadedFile on the service as well).
On the service, just change the arguments of your method and perform something "like this":
[HttpPost]
public HttpResponseMessage Upload(UploadedFile file)
{
if (!ModelState.IsValid)
...
if (file == null)
...
string destinationPath = Path.Combine(_whateverPath, file.FileFullName);
File.WriteAllBytes(destinationPath, file.Data);
}
Hope it helped you having an idea about what to do and what you're actually doing wrong. I've exposed something similar based in my experience.
EDIT: I've actually uploaded an example with both sides working: a simple .NET Core console app which retrieves a file and sends it through POST and a basic WebAPI2 service with a simple controller to retrieve the data. Both ready to go and tested working! Download it here.
Enjoy.

Adding an image to Azure blob storage using ASP.NET Web API fails

I have an Azure blob container for storing images. I also have a suite of ASP.NET Web API methods for adding / deleting / listing the blobs in this container. This all works if I upload the images as files. But I now want to upload the images as a stream and am getting an error.
public async Task<HttpResponseMessage> AddImageStream(Stream filestream, string filename)
{
try
{
if (string.IsNullOrEmpty(filename))
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest));
}
BlobStorageService service = new BlobStorageService();
await service.UploadFileStream(filestream, filename, "image/png");
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
catch (Exception ex)
{
base.LogException(ex);
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest));
}
The code for adding a new image to the blob container as a stream looks like this.
public async Task UploadFileStream(Stream filestream, string filename, string contentType)
{
CloudBlockBlob blockBlobImage = this._container.GetBlockBlobReference(filename);
blockBlobImage.Properties.ContentType = contentType;
blockBlobImage.Metadata.Add("DateCreated", DateTime.UtcNow.ToLongDateString());
blockBlobImage.Metadata.Add("TimeCreated", DateTime.UtcNow.ToLongTimeString());
await blockBlobImage.UploadFromStreamAsync(filestream);
}
And finally here's my unit test that is failing.
[TestMethod]
public async Task DeployedImageStreamTests()
{
string blobname = Guid.NewGuid().ToString();
//Arrange
MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes($"This is a blob called {blobname}."))
{
Position = 0
};
string url = $"http://mywebapi/api/imagesstream?filestream={stream}&filename={blobname}";
Console.WriteLine($"DeployedImagesTests URL {url}");
HttpContent content = new StringContent(blobname, Encoding.UTF8, "application/json");
var response = await ImagesControllerPostDeploymentTests.PostData(url, content);
//Assert
Assert.IsNotNull(response);
Assert.IsTrue(response.IsSuccessStatusCode); //fails here!!
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
The error I am getting is Value cannot be null.
Parameter name: source
Is this the correct way to upload an image stream to Azure blob storage using Web API? I have it working with image files without a problem, and only getting this problem now that I'm trying to upload using streams.
Is this the correct way to upload an image stream to Azure blob storage using Web API? I have it working with image files without a problem, and only getting this problem now that I'm trying to upload using streams.
According to your description and error message, I found you send your stream data in your url to the web api.
According to this article:
Web API uses the following rules to bind parameters:
If the parameter is a "simple" type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from a string. (More about type converters later.)
For complex types, Web API tries to read the value from the message body, using a media-type formatter.
In my opinion, the stream is a complex types, so I suggest you could post it as body to the web api.
Besides, I suggest you could crate a file class and use Newtonsoft.Json to convert it as json as message's content.
More details, you could refer to below codes.
File class:
public class file
{
//Since JsonConvert.SerializeObject couldn't serialize the stream object I used byte[] instead
public byte[] str { get; set; }
public string filename { get; set; }
public string contentType { get; set; }
}
Web Api:
[Route("api/serious/updtTM")]
[HttpPost]
public void updtTM([FromBody]file imagefile)
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse("aaaaa");
var client = storageAccount.CreateCloudBlobClient();
var container = client.GetContainerReference("images");
CloudBlockBlob blockBlobImage = container.GetBlockBlobReference(imagefile.filename);
blockBlobImage.Properties.ContentType = imagefile.contentType;
blockBlobImage.Metadata.Add("DateCreated", DateTime.UtcNow.ToLongDateString());
blockBlobImage.Metadata.Add("TimeCreated", DateTime.UtcNow.ToLongTimeString());
MemoryStream stream = new MemoryStream(imagefile.str)
{
Position=0
};
blockBlobImage.UploadFromStreamAsync(stream);
}
Test Console:
using (var client = new HttpClient())
{
string URI = string.Format("http://localhost:14456/api/serious/updtTM");
file f1 = new file();
byte[] aa = File.ReadAllBytes(#"D:\Capture2.PNG");
f1.str = aa;
f1.filename = "Capture2";
f1.contentType = "PNG";
var serializedProduct = JsonConvert.SerializeObject(f1);
var content = new StringContent(serializedProduct, Encoding.UTF8, "application/json");
var result = client.PostAsync(URI, content).Result;
}

Categories

Resources