Send Data to Web API using C# - c#

I was using the following code to send fromData contains 2 values (File and String) to a WebAPI using javascript.
var formData = new FormData();
formData.append('name', 'previewImg');
formData.append('upload', $('input[type=file]')[0].files[0]);
$.ajax({
url: 'WebAPI url',
data: formData,
contentType: false,
processData: false,
// ... Other options like success and etc
})
I want to do the same thing using C# Windows Application, I need to write a Method accepts 2 paramters (FilePath and String) then send the file and the string to WebAPI.
I tried the following code but it returns an error from the service( I am trying to contact with koemei upload service) , although it works fine when I call it from Js :
void SendData(string filepath,string name){
var url = "URL";
HttpContent fileContent = new ByteArrayContent(System.IO.File.ReadAllBytes(filepath));
using (var client = new HttpClient())
{
using (var formData = new MultipartFormDataContent())
{
formData.Add(fileContent, "upload");
formData.Add(new StringContent(name), "name");
//call service
var response = client.PostAsync(url, formData).Result;
if (!response.IsSuccessStatusCode)
{
throw new Exception();
}
else
{
if (response.Content.GetType() != typeof(System.Net.Http.StreamContent))
throw new Exception();
var stream = response.Content.ReadAsStreamAsync();
var content = stream.Result;
var path = #"name.txt";
using (var fileStream = System.IO.File.Create(path))
{
content.CopyTo(fileStream);
}
}
}
}
}

Here is a sample
private List<ByteArrayContent> GetFileByteArrayContent(HashSet<string> files)
{
List<ByteArrayContent> list = new List<ByteArrayContent>();
foreach (var file in files)
{
var fileContent = new ByteArrayContent(File.ReadAllBytes(file));
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = Path.GetFileName(file)
};
list.Add(fileContent);
}
return list;
}
private List<ByteArrayContent> GetFormDataByteArrayContent(NameValueCollection collection)
{
List<ByteArrayContent> list = new List<ByteArrayContent>();
foreach (var key in collection.AllKeys)
{
var dataContent = new ByteArrayContent(Encoding.UTF8.GetBytes(collection[key]));
dataContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
Name = key
};
list.Add(dataContent);
}
return list;
}
And here is how to post the data and files
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/xml"));//set how to get data
using (var content = new MultipartFormDataContent())//post by content type multipart/form-data
{
NameValueCollection dataCollection;//the datas you want to post
HashSet<string> filePaths;//the files you want to post
var formDatas = this.GetFormDataByteArrayContent(dataCollection);//get collection
var files = this.GetFileByteArrayContent(filePaths);//get collection
Action<List<ByteArrayContent>> act = (dataContents) =>
{//declare an action
foreach (var byteArrayContent in dataContents)
{
content.Add(byteArrayContent);
}
};
act(formDatas);//process act
act(files);//process act
try
{
var result = client.PostAsync(this.txtUrl.Text, content).Result;//post your request
}
catch (Exception ex)
{
//error
}
}
}

Related

Can't get angular/Core api to download a pdf into a new window

I am trying to open a new tab and display a downloaded pdf from Core 2.2 web API in angular 9
public GeneratePdf(id: string): Observable<Blob> {
return this.http.get( this.urlPdf + '?id=' + id, { responseType: 'blob' });
}
this.dataProvider.GeneratePdf(id).subscribe({
next: (blob) => {
const blobpart = new Blob([blob], { type: 'application/pdf' });
var fileUrl = URL.createObjectURL(blobpart);
let win: any = this.getWindow();
win.open(fileUrl);
},
error: (err) => this.error.handelHttpError(err),
});
API
[HttpGet]
[Route("PDF")]
public async Task<HttpResponseMessage> PDF(Guid Id) {
_logger.LogInformation("Request:" + Request.GetDisplayUrl());
var endpoint = _appSettings.PdfEndpoint;
try {
var param = await _dal.GetPDFParameters(Id, endpoint);
// Get PDF stream
HttpResponseMessage response = await client.GetAsync(param.EndpontUrl);
if(response.IsSuccessStatusCode) {
using(HttpContent content = response.Content) {
var memStream = new MemoryStream();
Stream sourceStream = await content.ReadAsStreamAsync();
sourceStream.CopyTo(memStream);
var result = new HttpResponseMessage(HttpStatusCode.OK) {
Content = new ByteArrayContent(memStream.ToArray())
};
result.Content.Headers.ContentDisposition =
new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment") {
FileName = Id.ToString() + ".pdf"
};
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
return result;
}
} else {
var result = new HttpResponseMessage(HttpStatusCode.BadRequest);
return result;
}
} catch(Exception ex) {
_logger.LogError(ex, "Exception error");
var result = new HttpResponseMessage(HttpStatusCode.InternalServerError);
return result;
}
}
it doesn't throw an error, it just opens a json object in the new tab, and the size of the object looks too small as the pdf content is over 3k bytes
{"version":{"major":1,"minor":1,"build":-1,"revision":-1,"majorRevision":-1,"minorRevision":-1},"content":{"headers":[{"key":"Content-Disposition","value":["attachment; filename=bd94ee98-65be-4c4f-a001-abecaf1a0644.pdf"]},{"key":"Content-Type","value":["application/octet-stream"]}]},"statusCode":200,"reasonPhrase":"OK","headers":[],"requestMessage":null,"isSuccessStatusCode":true}
update, there was a small error on the Blob, I was not passing in the blobpart to the url.CreateObjecturl Now the app loads the new tab, but states an invalid pdf as there is no content. I know the pdf bytes are going into the content of the api result as i have checked it. and converted it to a string to ensure it's a pdf, which it is.
Thanks for taking the time too look.

HttpClient PostAsycn Always returns 404 Not Found but controller works with Postman and Javascript application

I have a Post Web Api Method in a controller and it's already working, I've been testing the method sending file from postman and a my own web application and it works but now I'm trying to send file from console application using Httpclient but always get 404.
Controller Method
public async Task<IHttpActionResult> Post()
{
FileServerConfig config = FileServerConfiguration.ObtenerConfiguracion(ConfigurationManager.AppSettings);
var path = string.Empty;
try
{
if (!Request.Content.IsMimeMultipartContent())
{
return StatusCode(HttpStatusCode.UnsupportedMediaType);
}
var filesReadToProvider = await Request.Content.ReadAsMultipartAsync();
foreach (var stream in filesReadToProvider.Contents)
{
IFileServerWrapper _cliente = FileServerConfiguration.CrearCliente(config);
var name = stream.Headers.ContentDisposition.FileName.Replace("\"", "").Replace("\\", "");
var fileUploaded = await _cliente.FileUpload(path, await stream.ReadAsStreamAsync(), name, false);
}
return Ok();
}
catch (HttpException httpex)
{
if (httpex.GetHttpCode() == (int)HttpStatusCode.Conflict)
{
return Conflict();
}
else
{
return InternalServerError(httpex);
}
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
Console application
static void Main(string[] args)
{
string filePath = #"C:\Software\itextTifftoPDF.rar";
using (var client = new HttpClient())
using (var content = new MultipartFormDataContent())
{
// Make sure to change API address
//client.BaseAddress = new Uri("http://localhost:80/FileServerAPI/");
client.DefaultRequestHeaders.Add("Accept-Language", "en-GB,en-US;q=0.8,en;q=0.6,ru;q=0.4");
client.DefaultRequestHeaders.CacheControl = CacheControlHeaderValue.Parse("no-cache");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Add first file content
var fileContent1 = new ByteArrayContent(File.ReadAllBytes(filePath));
fileContent1.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
FileName = "itextTifftoPDF.rar"
};
content.Add(fileContent1);
// Make a call to Web API
var result = client.PostAsync("http://localhost/FileServerAPI/api/File", content).Result;
Console.WriteLine(result.StatusCode);
Console.ReadLine();
}
}
Finally I found a solution. the code is ugly because it was made just to be sure it works but I'll hope it may help others with the same problem.
string filePath = #"C:\temp\webdavtest.txt";
using (HttpClient httpClient = new HttpClient())
{
using (MultipartFormDataContent content =
new MultipartFormDataContent())
{
using (FileStream stream = File.Open(
filePath, FileMode.Open, FileAccess.Read))
{
using (StreamContent streamConent =
new StreamContent(stream))
{
content.Add(
streamConent, "webdavtest.txt", "webdavtest.txt");
var result = await httpClient.PostAsync("http://localhost:3983/api/File?path=/nivel5/", content);
return result.StatusCode.ToString();
}
}
}
}

Client code to upload file via ASP.NET MVC WebApi

I am trying to write code to upload file(s) by WinForm app to WebApi.
The WebApi code is like:
[HttpPost]
[Route("UploadEnvelope")]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
public Task<HttpResponseMessage> PostUploadEnvelope()
{
HttpRequestMessage request = this.Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/uploads");
var provider = new MultipartFormDataStreamProvider(root);
var task = request.Content.ReadAsMultipartAsync(provider).ContinueWith<HttpResponseMessage>(o =>
{
foreach (MultipartFileData fileData in provider.FileData)
{
if (string.IsNullOrEmpty(fileData.Headers.ContentDisposition.FileName))
{
return Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted");
}
string fileName = fileData.Headers.ContentDisposition.FileName;
if (fileName.StartsWith("\"") && fileName.EndsWith("\""))
{
fileName = fileName.Trim('"');
}
if (fileName.Contains(#"/") || fileName.Contains(#"\"))
{
fileName = Path.GetFileName(fileName);
}
File.Move(fileData.LocalFileName, Path.Combine(root, fileName));
}
return new HttpResponseMessage()
{
Content = new StringContent("Files uploaded.")
};
}
);
return task;
}
But I am not sure how to call it and pass file in a client app.
static string UploadEnvelope(string filePath, string token, string url)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
// How to pass file here ???
var response = client.GetAsync(url + "/api/Envelope/UploadEnvelope").Result;
return response.Content.ReadAsStringAsync().Result;
}
}
Any help or suggestion is welcome. Thanks in advance!
First you are using Get method which is used for reading. You have to use Post instead.
Try the following:
public static string UploadEnvelope(string filePath,string token, string url)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
using (var content = new MultipartFormDataContent("Envelope" + DateTime.Now.ToString(CultureInfo.InvariantCulture)))
{
content.Add(new StreamContent(new MemoryStream(File.ReadAllBytes(filePath))), "filename", "filename.ext");
using (var message = await client.PostAsync(url + "/api/Envelope/UploadEnvelope", content))
{
var input = await message.Content.ReadAsStringAsync();
return "success";
}
}
}
}
Note: For a large file you have to change configuration on IIS web.config.

How to parse MultipartFormDataContent

I am writing a Web API service where I want to accept a file (image) and a serialized object (JSON) that contains key information about the image. Not having issues with the image part but when I add string content containing the deserialized object I am having issues in trying to determine which is which and act accordingly.
The client code looks like:
HttpClient client = new HttpClient();
MultipartFormDataContent content = new MultipartFormDataContent();
content.Add(new StreamContent(File.Open("c:\\MyImages\\Image00.jpg", FileMode.Open)), "image_file", "Image00.jpg");
ImageKeys ik = new ImageKeys { ImageId = "12345", Timestamp = DateTime.Now.ToString() };
JavaScriptSerializer js = new JavaScriptSerializer();
if (ik != null)
{
content.Add(new StringContent(js.Serialize(ik), Encoding.UTF8, "application/json"), "image_keys");
}
string uri = "http://localhost/MyAPI/api/MyQuery/TransferFile";
var request = new HttpRequestMessage()
{
RequestUri = new Uri(uri),
Method = HttpMethod.Post
};
request.Content = content;
string responseStr = "";
try
{
HttpResponseMessage result = client.SendAsync(request).Result;
string resultContent = string.Format("{0}:{1}", result.StatusCode, result.ReasonPhrase);
//
// Handle the response
//
responseStr = resultContent;
}
catch (Exception ex)
{
responseStr = ex.Message;
}
listBox1.Items.Add(responseStr);
So I include the image file first followed by a serialized object as StringContent. On the server side I am using the following code to parse the message.
HttpRequestMessage request = this.Request;
HttpResponseMessage ret = new HttpResponseMessage();
//
// Verify that this is an HTML Form file upload request
//
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = "c:\\tmp\\uploads";
if (!Directory.Exists(root))
{
Directory.CreateDirectory(root);
}
//
// Create a stream provider for setting up output streams that saves the output under c:\tmp\uploads
// If you want full control over how the stream is saved then derive from MultipartFormDataStreamProvider
// and override what you need.
//
MultipartFormDataStreamProvider streamProvider = new MultipartFormDataStreamProvider(root);
try
{
await request.Content.ReadAsMultipartAsync(streamProvider);
foreach (var file in streamProvider.Contents)
{
if (file.Headers.ContentDisposition.Name == "image_file")
{
FileInfo finfo = new FileInfo(streamProvider.FileData.First().LocalFileName);
string destFile = Path.Combine(root, streamProvider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", ""));
//
// File.Move cannot deal with duplicate files
// Ensure that the target does not exist.
//
if (File.Exists(destFile))
{
File.Delete(destFile);
}
File.Move(finfo.FullName, destFile);
}
else if (file.Headers.ContentDisposition.Name == "image_keys")
{
// deserialize key class
string str = file.ReadAsStringAsync().Result;
JavaScriptSerializer js = new JavaScriptSerializer();
ImageKeys ik = js.Deserialize<ImageKeys>(str);
}
}
ret.StatusCode = HttpStatusCode.OK;
ret.Content = new StringContent("File uploaded.");
}
catch (Exception ex)
{
ret.StatusCode = HttpStatusCode.UnsupportedMediaType;
ret.Content = new StringContent("File upload failed.");
}
return ret;
The foreach loop tries to process each item in the multipart content as a file but I want to treat the various content types separately but it is not clear to me how they are delineated.
Thanks
You can cast Content to MultipartFormDataContent and iterate thru it. Based on content type you can read it as a file or string. Example for string content type:
var dataContents = request.Content as MultipartFormDataContent;
foreach (var dataContent in dataContents)
{
var name = dataContent.Headers.ContentDisposition.Name;
var value = dataContent.ReadAsStringAsync().Result;
...
}

best practice for uploading images to azure blob

I am building a mobile app using xamarin while the server is hosted in azure. I am uploading images in the following way:
Client:
public static async Task<string> UploadImage (string url, byte[] imageData)
{
var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(imageData);
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = Guid.NewGuid() + ".Png"
};
content.Add(fileContent);
using (var client = new HttpClient())
{
try
{
HttpResponseMessage msg = await client.PutAsync (url, content);
if(msg.StatusCode == System.Net.HttpStatusCode.OK)
{
return msg.Headers.GetValues ("ImageUrl").First();
}
return string.Empty;
}
catch (Exception ex)
{
return string.Empty;
}
}
}
and here is the server code:
[HttpPut]
public async Task<HttpResponseMessage> PostNewDishImage(string imageID)
{
try
{
_dishImagescontainer = BlobStorageHandler.GetContainer("dishuserimages");
if (!Request.Content.IsMimeMultipartContent("form-data"))
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.UnsupportedMediaType));
}
var provider = new BlobStorageProvider(_dishImagescontainer);
await Request.Content.ReadAsMultipartAsync(provider);
IList<string> urls = provider.Urls;
if (urls.Count > 0)
{
var response = new HttpResponseMessage();
response.StatusCode = HttpStatusCode.OK;
response.Headers.Add("ImageUrl", urls[0]);
return response;
}
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
catch (System.Exception e)
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError) { ReasonPhrase = e.ToString() };
}
}
It works fine but I don't like the way I am returning the new imageurl back to the client (through the http headers) I have tried some other ways but this is the best one so far :)
Does anyone have any better ideas?
Thanks
Returning data to the client in an HTTP header like that does have a bit of a smell. How about returning a DTO serialized to JSON?
An example DTO class:
public class ImageUploadResponse
{
public string Url { get; set; }
}
Then change your server side code to something like this:
var responseDto = new ImageUploadResponse { Url = urls[0] };
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(JsonConvert.SerializeObject(responseDto),
Encoding.UTF8, "application/json")
};
which will send the results back to the client in the content of the HTTP response as JSON. Then the client can parse the JSON into an object (or not) as you see fit. This approach will also be more friendly to making such a call from JavaScript in the future if you desire because the result is standard JSON.

Categories