I'm uploading a image using Web API.
public IHttpActionResult UploadImage()
{
FileDescription filedescription = null;
var allowedExt=new string[]{".png",".jpg",".jpeg"};
var result = new HttpResponseMessage(HttpStatusCode.OK);
if (Request.Content.IsMimeMultipartContent())
{
Request.Content.LoadIntoBufferAsync().Wait();
var imgTask = Request.Content.ReadAsMultipartAsync<MultipartMemoryStreamProvider>(new MultipartMemoryStreamProvider()).ContinueWith((task) =>
{
MultipartMemoryStreamProvider provider = task.Result;
var content = provider.Contents.ElementAt(0);
Stream stream = content.ReadAsStreamAsync().Result;
Image image = Image.FromStream(stream);
var receivedFileName = content.Headers.ContentDisposition.FileName.Replace("\"", string.Empty);
var getExtension = Path.GetExtension(receivedFileName);
if(allowedExt.Contains(getExtension.ToLower())){
string newFileName = "TheButler" + DateTime.Now.Ticks.ToString() + getExtension;
var originalPath = Path.Combine("Folder Path Here", newFileName);
image.Save(originalPath);
var picture = new Images
{
ImagePath = newFileName
};
repos.ImagesRepo.Insert(picture);
repos.SaveChanges();
filedescription = new FileDescription(imagePath + newFileName, picture.Identifier);
}
});
if (filedescription == null)
{
return ResponseMessage(Request.CreateResponse(HttpStatusCode.InternalServerError, new { message="some error msg."}));
}
else
{
return ResponseMessage(Request.CreateResponse(HttpStatusCode.OK, filedescription));
}
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, new { message = "This request is not properly formatted" }));
}
}
Above code I have used to save image, But every time filedescription even after image is successfully saved. What's wrong I'm doing here.
I want to return filedescription after image save.
You are using a lot of async methods in the wrong way. You should never make a call to an async method and then wait for it to finish in a synchronous manner (e.g. using Wait() or Result).
If an API exposes an async method, then await for it, turning also the calling method into an async method.
The reason why your filedescription variable is always null, is because you are checking it before you continuation has finished. This is truly a bad practice: if you need a value that is a result of an async operation, then you have to wait for it to finish, but in this case, using the await keyword.
This is the correct way of calling async methods and wait (asynchronously) for them to finish:
public async Task<IHttpActionResult> UploadImage()
{
FileDescription filedescription = null;
var allowedExt = new string[] { ".png", ".jpg", ".jpeg" };
var result = new HttpResponseMessage(HttpStatusCode.OK);
if (Request.Content.IsMimeMultipartContent())
{
await Request.Content.LoadIntoBufferAsync();
var provider = await Request.Content.ReadAsMultipartAsync(new MultipartMemoryStreamProvider());
var content = provider.Contents.ElementAt(0);
Stream stream = await content.ReadAsStreamAsync();
Image image = Image.FromStream(stream);
var receivedFileName = content.Headers.ContentDisposition.FileName.Replace("\"", string.Empty);
var getExtension = Path.GetExtension(receivedFileName);
if (allowedExt.Contains(getExtension.ToLower()))
{
string newFileName = "TheButler" + DateTime.Now.Ticks.ToString() + getExtension;
var originalPath = Path.Combine("Folder Path Here", newFileName);
image.Save(originalPath);
var picture = new Images
{
ImagePath = newFileName
};
repos.ImagesRepo.Insert(picture);
repos.SaveChanges();
filedescription = new FileDescription(imagePath + newFileName, picture.Identifier);
}
if (filedescription == null)
{
return ResponseMessage(Request.CreateResponse(HttpStatusCode.InternalServerError, new { message = "some error msg." }));
}
else
{
return ResponseMessage(Request.CreateResponse(HttpStatusCode.OK, filedescription));
}
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, new { message = "This request is not properly formatted" }));
}
}
Related
I am wrote the code to POST from my web application but it is sending 400 bad request.
See the my controller class:
[HttpPost]
public async Task<IActionResult> CreateAgent(AgentCreateDto agentCreateDto)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://localhost:44331/api/");
var responseTask = client.PostAsJsonAsync("Agent/CreateAgent", agentCreateDto);
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var newAgentAdd = JsonConvert.DeserializeObject<AgentCreateDto>(await result.Content.ReadAsStringAsync());
var newAgent = newAgentAdd;
TempData["SuccessMessage"] = $"Agent {newAgent.FirstName} was successfully created.";
return RedirectToAction("AgentLists");
//return RedirectToAction("GetAgentById", new { AgentId = newAgent.usersId});
}
}
return View();
}
Here is below the problem image please see for the reference.
Here is api code for that I am send the request:
[HttpPost("CreateAgent")]
public async Task<IActionResult> CreateAgent([FromForm]AgentCreateDto agentCreateDto)
{
if (!ModelState.IsValid)
{
return BadRequest("Invalid model object");
}
if (agentCreateDto.ProfileImage != null)
{
string uniqueFileName = UploadProfileImage(agentCreateDto.ProfileImage);
agentCreateDto.ProfileNewImage = uniqueFileName;
}
var createAgent = await _userService.CreateAgent(agentCreateDto);
//_logger.LogInformation($"User added to the for file test. ");
return Created("", new
{
Id = createAgent.UsersId,
Message = "Agent created Successfully",
Status = "Success",
UserType = createAgent.UserType
});
}
And for image uploading code at the API side:
private string UploadProfileImage(IFormFile ProfileImage)
{
string uniqueFileName = null;
if (ProfileImage != null)
{
string uploadsFolder = Path.Combine(_webHostEnvironment.ContentRootPath, "ProfileImage");
uniqueFileName = Guid.NewGuid().ToString() + "_" + ProfileImage.FileName;
string filePath = Path.Combine(uploadsFolder, uniqueFileName);
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
ProfileImage.CopyTo(fileStream);
}
}
return uniqueFileName;
}
The correct way to post file and additional data by HttpClient like below:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://localhost:44331/api/");
var formContent = new MultipartFormDataContent();
formContent.Add(new StringContent(agentCreateDto.Id.ToString()), nameof(agentCreateDto.Id));
formContent.Add(new StringContent(agentCreateDto.Message), nameof(agentCreateDto.Message));
formContent.Add(new StringContent(agentCreateDto.UserType), nameof(agentCreateDto.UserType));
//other proerties....
formContent.Add(new StreamContent(agentCreateDto.ProfileImage.OpenReadStream()), nameof(agentCreateDto.ProfileImage), Path.GetFileName(agentCreateDto.ProfileImage.FileName));
//change PostAsJsonAsync to PostAsync.....
var responseTask = client.PostAsync("/Agent/CreateAgent", formContent);
responseTask.Wait();
var result = responseTask.Result;
//more code...
}
I'm creating a REST API in which accepts a JSON string with 3 different fileUpload in form data.
Here is my API controller post method
public async Task<IHttpActionResult> Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
return StatusCode(HttpStatusCode.UnsupportedMediaType);
}
var filesReadToProvider = await Request.Content.ReadAsMultipartAsync();
var obj = new SubmitReportModel();
var enggCommentImg = new List<FileModel>();
var concernAreaImg = new List<FileModel>();
var remarkImge = new List<FileModel>();
string UploadPath = ("~/Uploads/ReportsData");
Functions.CreateIfMissing(System.Web.Hosting.HostingEnvironment.MapPath(UploadPath));
foreach (var item in filesReadToProvider.Contents)
{
if (item.Headers.ContentDisposition.Name.Contains("jsonKey"))
{
var jsson = await item.ReadAsStringAsync();
obj = Newtonsoft.Json.JsonConvert.DeserializeObject<SubmitReportModel>(jsson);
}
else if (item.Headers.ContentDisposition.Name.Contains("enggCommentImg"))
{
_ = new FileModel();
var fileDataByteArray = await item.ReadAsByteArrayAsync();
FileModel objfile = await SaveFileAsync(fileDataByteArray);
enggCommentImg.Add(objfile);
}
else if (item.Headers.ContentDisposition.Name.Contains("concernAreaImg"))
{
_ = new FileModel();
var fileDataByteArray = await item.ReadAsByteArrayAsync();
FileModel objfile = await SaveFileAsync(fileDataByteArray);
concernAreaImg.Add(objfile);
}
else if (item.Headers.ContentDisposition.Name.Contains("remarkImge"))
{
_ = new FileModel();
var fileDataByteArray = await item.ReadAsByteArrayAsync();
FileModel objfile = await SaveFileAsync(fileDataByteArray);
remarkImge.Add(objfile);
}
}
return Ok();
}
private async Task<FileModel> SaveFileAsync(byte[] fileDataByteArray)
{
string uploadPath = ("~/Uploads/ReportsData");
var objfile = new FileModel();
var filename = uploadPath + "/" + ApiHelper.GetTimestamp() + ".jpg";
using (FileStream fs = new FileStream(System.Web.Hosting.HostingEnvironment.MapPath(filename), FileMode.OpenOrCreate, FileAccess.Write))
{
await fs.WriteAsync(fileDataByteArray, 0, fileDataByteArray.Length).ConfigureAwait(true);
}
objfile.Name = filename;
objfile.Type = "jpg";
return objfile;
}
This API works fine I got JSON plus multiple files in each file object as well.
It works very well when I debug using breakpoints and parse each step using f10. But when I try it without breakpoints, it only saves the first file.
Here is how I'm calling it using Postman:
I tried the same with making it synchronous but still, I get the same result - saving only one file.
Here is my synchronous POST API code
public object Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
return StatusCode(HttpStatusCode.UnsupportedMediaType);
}
//var filesReadToProvider =Request.Content.ReadAsMultipartAsync().GetAwaiter().GetResult();
var filesReadToProvider = Task.Run(async () => await Request.Content.ReadAsMultipartAsync()).Result;
var obj = new SubmitReportModel();
var enggCommentImg = new List<FileModel>();
var concernAreaImg = new List<FileModel>();
var remarkImge = new List<FileModel>();
string UploadPath = ("~/Uploads/ReportsData");
Functions.CreateIfMissing(System.Web.Hosting.HostingEnvironment.MapPath(UploadPath));
foreach (var item in filesReadToProvider.Contents)
{
if (item.Headers.ContentDisposition.Name.Contains("jsonKey"))
{
var jsson = Task.Run(async () => await item.ReadAsStringAsync()).Result;
obj = Newtonsoft.Json.JsonConvert.DeserializeObject<SubmitReportModel>(jsson);
}
else if (item.Headers.ContentDisposition.Name.Contains("enggCommentImg"))
{
_ = new FileModel();
var fileDataByteArray = Task.Run(async () => await item.ReadAsByteArrayAsync()).Result;
if (fileDataByteArray.Length > 0)
{
FileModel objfile = SaveFiles(fileDataByteArray);
enggCommentImg.Add(objfile);
}
}
else if (item.Headers.ContentDisposition.Name.Contains("concernAreaImg"))
{
_ = new FileModel();
var corsrnArr = Task.Run(async () => await item.ReadAsByteArrayAsync()).Result;
if (corsrnArr.Length > 0)
{
FileModel objfile = SaveFiles(corsrnArr);
concernAreaImg.Add(objfile);
}
}
else if (item.Headers.ContentDisposition.Name.Contains("remarkImge"))
{
_ = new FileModel();
var remarkarr = Task.Run(async () => await item.ReadAsByteArrayAsync()).Result;
if (remarkarr.Length > 0)
{
FileModel objfile = SaveFiles(remarkarr);
remarkImge.Add(objfile);
}
}
}
return 1;
}
private FileModel SaveFiles(byte[] fileDataByteArray)
{
string uploadPath = ("~/Uploads/ReportsData");
var objfile = new FileModel();
var filename = uploadPath + "/" + ApiHelper.GetTimestamp() + ".jpg";
using (FileStream fs = new FileStream(System.Web.Hosting.HostingEnvironment.MapPath(filename), FileMode.OpenOrCreate, FileAccess.Write))
{
fs.Write(fileDataByteArray, 0, fileDataByteArray.Length);
}
objfile.Name = filename;
objfile.Type = "jpg";
return objfile;
}
I'm unable to figure what exactly wrong step I made in it as there is no error in it.
UPDATE:
As theGeneral advised, I do recheck all steps and created a log so
found that all images were getting upload within a second so all of
them receive the same timestamp as name, and as a result, only one
file was getting overwrite again and again.
Keeping this thread as it is as this API work for files + other data
(JSON string in my case) and may get helpful for others
I am working a chat application and when when I upload a image with button click then image doesn't received to another device only sender device showing it.
MyButton click code:
public async void Image_Clicked(object sender, EventArgs e)
{
try
{
file = await CrossMedia.Current.PickPhotoAsync(new Plugin.Media.Abstractions.PickMediaOptions
{
CompressionQuality = 50,
PhotoSize = Plugin.Media.Abstractions.PhotoSize.Medium
});
FileName = file.Path;
FileName = FileName.Substring(FileName.LastIndexOf("/") + 1);
var content = new MultipartFormDataContent();
//IFolder rootFolder = await FileSystem.Current.GetFolderFromPathAsync(FileName);
//var filee = await rootFolder.GetFileAsync(filename2);
//Stream stream = await filee.OpenAsync(FileAccess.Read);
content.Add(new StreamContent(file.GetStream()),
"\"file\"",
$"\"{file.Path}\"");
byteData = Model.Convert.ToByteArray(FileName);
var im = ImageSource.FromStream(this.file.GetStream);
using (var memoryStream = new MemoryStream())
{
activityIndicator.IsRunning = true;
file.GetStream().CopyTo(memoryStream);
file.Dispose();
// return memoryStream.ToArray();
uploadedFilename = await AzureStorage.UploadFileAsync(ContainerType.Image, new MemoryStream(memoryStream.ToArray()));
}
if (!string.IsNullOrWhiteSpace(uploadedFilename))
{
var imageData = await AzureStorage.GetFileAsync(ContainerType.Image, uploadedFilename);
var img = ImageSource.FromStream(() => new MemoryStream(imageData));
TextContainer.Add(new MessageText { imgsource = img });
activityIndicator.IsRunning = false;
}
}
catch( Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
}
I am Also using ObservableCollection but unable to get image to second device:
public ObservableCollection<MessageText> TextContainer { get; set; } = new ObservableCollection<MessageText>();
Upload fileCode which I have used to upload my file:
public static async Task<string> UploadFileAsync(ContainerType containerType, Stream stream)
{
var container = GetContainer(containerType);
await container.CreateIfNotExistsAsync();
var name = Guid.NewGuid().ToString();
var fileBlob = container.GetBlockBlobReference(name);
await fileBlob.UploadFromStreamAsync(stream);
return name;
}
I have used SignalR and its working fine for me
I'm getting a web response via an API which converts file (in this case, Powerpoint presentations to PDF).
I do get the response in a string, but when saving this string to a file or a stream (which ends up being saved in a file anyways) I always end up with a blank file, its size is always well over 0 bytes though.
Here's the class that calls the API:
public class CloudConvert
{
private string apiKey;
private static object ProcessResponse;
public CloudConvert(string apiKey)
{
this.apiKey = apiKey;
}
public async Task<string> Convert(string inputFormat, string outputFormat, string fileUrl)
{
var processUrl = await CreateProcess(inputFormat, outputFormat);
return await Upload(processUrl, fileUrl, outputFormat);
}
private async Task<string> CreateProcess(string inputFormat, string outputFormat)
{
var request = new
{
apikey = apiKey,
inputformat = inputFormat,
outputformat = outputFormat
};
var json = await PostJson("https://api.cloudconvert.com/process", request);
dynamic obj = JObject.Parse(json);
ProcessResponse = obj;
return "https:" + obj.url;
}
private static async Task<string> Upload(string processUrl,
string fileUrl, string outputFormat)
{
var request = new
{
input = "download",
file = fileUrl,
outputformat = outputFormat,
download = "false",
wait = "true",
save = "false"
};
return await PostJson(processUrl, request);
}
private static async Task<string> PostJson(string url, object data)
{
var parameters = JsonConvert.SerializeObject(data);
using (var wc = new WebClient())
{
wc.Headers[HttpRequestHeader.ContentType] = "application/json";
try
{
return await wc.UploadStringTaskAsync(url, "POST", parameters);
}
catch (WebException ex)
{
return string.Empty;
}
}
}
}
And how I'm invoking it:
Task<string> task = Task.Run(async () =>
{
return await cloudConvert.Convert("ppt", "pdf", "http://host.com/myfile.ppt");
});
//I have the PDF in the response, as a string.
var pdfContent = task.Result;
//I think it's here that I'm not handling the resulting string
//properly, ending in a blank file. I've tried writing it to a local file as well, same issue.
using (MemoryStream stream = new MemoryStream())
{
StreamWriter writer = new StreamWriter(stream);
writer.Write(pdfContent);
writer.Flush();
stream.Seek(0, SeekOrigin.Begin);
//This below is something that saves to an Azure storage container...
//filePath = fileStorage.SaveFile($"{fileGeneratedName}.pdf", stream);
//But this fails as well. Blank file
using (FileStream fs = new FileStream(#"c:\tmp\output.pdf", FileMode.OpenOrCreate))
{
stream.CopyTo(fs);
fs.Flush();
}
}
So regardless of how I'm trying to save the returned content, it seems my encoding is wrong. I noticed that the resulting blank file always has the same number of pages than the input Powerpoint presentation.
Any ideas?
Have your convert function return an HttpResponseMessage:
public async Task<HttpResponseMessage> convert()
do your conversion into, say, a memory stream, and then:
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(outStream.GetBuffer(),0,(int)outStream.Length)
};
result.Content.Headers.ContentDisposition =
new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileName = fname
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return result;
I have this following method that I use to download a file's content:
public async Task<String> DownloadFileService(String filePath, string id)
{
string resposta = string.Empty;
try
{
using (var httpClient = new HttpClient { BaseAddress = Constants.baseAddress })
{
string token = App.Current.Resources["token"] as string;
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
string fname = Path.GetFileName(filePath);
string path = Path.GetDirectoryName(filePath);
path = path.Replace(fname, "");
StorageFolder folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(Constants.DataDirectory + "\\" + path, CreationCollisionOption.OpenIfExists);
StorageFile imgFile = await folder.CreateFileAsync(fname, CreationCollisionOption.ReplaceExisting);
using (var response2 = await httpClient.GetAsync("file?fileId=" + id))
{
Stream imageStream = await response2.Content.ReadAsStreamAsync();
byte[] bytes = new byte[imageStream.Length];
imageStream.Read(bytes, 0, (int)imageStream.Length);
await FileIO.WriteBytesAsync(imgFile, bytes);
resposta = Convert.ToBase64String(bytes);
}
}
return resposta;
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
}
}
I would like to know how I can call this multiple times to download several files at same time and wait until all files are downloaded, then do other stuff.
EDIT
After this suggestion I tried creating the following method:
public async void checkFilesExist(JsonArray array, string path)
{
List<Document> list = new List<Document>();
ObjectsService obj = new ObjectsService();
List<Task> ts = new List<Task>();
foreach (var item in array)
{
JsonObject newDoc;
JsonObject.TryParse(item.Stringify(), out newDoc);
if (newDoc.ContainsKey("libraryType") || !newDoc.ContainsKey("fileName"))
continue;
string name = newDoc["fileName"].GetString();
string id = newDoc["_id"].GetString();
File file = new File(name);
file.id = id;
Document doc = file;
doc.Parent = Document.FromPath(path);
path = path.Replace("/", "\\");
StorageFolder folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(Constants.DataDirectory + "\\" + path, CreationCollisionOption.OpenIfExists);
try
{
await folder.GetFileAsync(file.Name);
}
catch (Exception e)
{
list.Add(doc);
Task x = obj.DownloadFileService(doc.GetFullPath(), file.id);
ts.Add(x);
Debug.WriteLine(" Ex: " + e.Message);
}
}
try
{
Task.WaitAll(ts.ToArray());
Debug.WriteLine("AFTER THrEADS");
}
catch (Exception e)
{
Debug.WriteLine("Ex2: " + e.Message);
}
}
What this does is, with a response in json I get from a webservice listing some files, I check if they already exist in localfolder.
If they don't I call the method I had at start of the question.
I then have a list of tasks, and I add the call of the DownloadFileService() as a new task in the list, after that I do the Task.WaitAll() to wait for the downloads to finish.
Using fiddler I see the downloads all start, but for some reason my code doesn't stop at Task.WaitAll(), it just keeps going and it starts to use the files that are still being downloaded, creating a bunch of problems :D
you can use Task.WaitAll. It waits for all of the provided Task objects to complete execution.
var t1 = DownloadFileService("file1", "1");
var t2 = DownloadFileService("file2", "2");
var t3 = DownloadFileService("file3", "3");
Tasks.WaitAll(t1, t2, t3);
Or you can use :
await DownloadFileService("Path", "id");
await DownloadFileService("Path", "id");
await DownloadFileService("Path", "id");
await DownloadFileService("Path", "id");