Windows Store Apps, download multiple files with threads - c#

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

Related

multi-threaded foreach Api Post and write response to a folder

I have a api in which I need to post a request and write the response to individual txt files.
Each post request needs to go with a MaterialID which I am reading from a list. The list can contain anything from 1000 to 5000 MaterialIDs
I need a way to do multiple parallel requests.
The below code is what I currently have but is built more for synchronous request.
How do i go about doing parallel requests ?
//reads MaterialID from file
System.IO.StreamReader fileTXT =
new System.IO.StreamReader(#"C:\\Temp\Test.txt");
while ((line = fileTXT.ReadLine()) != null)
{
Material_Count.Add(line.Trim());
UpdateLogTxt("Material_Count = " + line.ToString() + " ");
}
fileTXT.Close();
//loops through MaterialID list and starts post request
foreach (var item in Material_Count)
{
try
{
UpdateLogTxt(" Submitting = " + item.ToString());
var Set = Properties.Settings.Default;
using (var httpClient = new HttpClient())
{
string FCSEndPoint = #"https://www.api.co.za";
string FCSToken = "";
FCSToken = textBoxDescription.Text.Trim();
string uri = $"{FCSEndPoint}/material/pageIndex=" + item.ToString() + "";
HttpResponseMessage response = null;
httpClient.BaseAddress = new Uri(uri);
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("SessionToken", FCSToken);
AuthenticationHeaderValue headerObj = new AuthenticationHeaderValue("SessionToken", FCSToken);
httpClient.DefaultRequestHeaders.Authorization = headerObj;
var TaskObj = httpClient.PostAsJsonAsync(uri, String.Empty);
TaskObj.Wait();
HttpResponseMessage messageOut = TaskObj.Result;
response = TaskObj.Result;
if (response.IsSuccessStatusCode)
{
var TaskObj2 = messageOut.Content.ReadAsStringAsync();
TaskObj2.Wait();
string ResponseStr = TaskObj2.Result;
//writes response to a txt file.
string fileName = #"C:\Temp\material\Material_ " + item.ToString() + " " + DateTime.Now.ToString("yyyyMMddmmss") + ".txt";
try
{
// Check if file already exists. If yes, delete it.
if (File.Exists(fileName))
{
File.Delete(fileName);
}
// Create a new file
using (StreamWriter sw = File.CreateText(fileName))
{
sw.WriteLine(ResponseStr.ToString());
}
}
catch (Exception ex)
{
UpdateLogTxt("Exception Write to file Failed = --- " + ex.ToString());
}
}
else if (response.StatusCode == System.Net.HttpStatusCode.Forbidden)
{
UpdateLogTxt("Response Failed (Forbidden) = --- " + response.StatusCode.ToString());
}
else
{
}
}
}
catch (HttpRequestException h)
{
UpdateLogTxt("HttpRequestException Send Failed = --- " + h.ToString());
}
catch (Exception ex)
{
UpdateLogTxt("Exception Send Failed = --- " + ex.ToString());
}
}
Replace the foreach by the Parallel.ForEach in the System.Threading.Tasks namespace
You can find handy sample codes here =>
Parallel.ForEach method
Here's an alternative using Microsoft's Reactive Framework - NuGet System.Reactive and then you can do this:
Start by collecting a couple of constants used in your code to make sure they happen on the UI thread and only once:
string fcsToken = textBoxDescription.Text.Trim();
string now = DateTime.Now.ToString("yyyyMMddmmss");
Now lets create a helper method to handle the HTTP call:
Task<HttpResponseMessage> PostAsJsonAsync(HttpClient hc, string item)
{
string fcsEndPoint = #"https://www.api.co.za";
string uri = $"{fcsEndPoint}/material/pageIndex={item}";
hc.BaseAddress = new Uri(uri);
hc.DefaultRequestHeaders.Accept.Clear();
hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("SessionToken", fcsToken);
AuthenticationHeaderValue authorization = new AuthenticationHeaderValue("SessionToken", fcsToken);
hc.DefaultRequestHeaders.Authorization = authorization;
return hc.PostAsJsonAsync(uri, String.Empty);
}
Now we can write the observable query:
IObservable<(string fileName, string responseText)> query =
from item in
File
.ReadLines(#"C:\\Temp\Test.txt")
.Select(x => x.Trim())
.ToObservable()
from output in
Observable
.Using(
() => new HttpClient(),
hc =>
from response in Observable.FromAsync(() => PostAsJsonAsync(hc, item))
where response.IsSuccessStatusCode
from responseText in Observable.FromAsync(() => response.Content.ReadAsStringAsync())
select
(
fileName: $#"C:\Temp\material\Material_ {item} {now}.txt",
responseText: responseText
))
select output;
And finally we have the subscription:
IDisposable subscription =
query
.Subscribe(x => File.WriteAllText(x.fileName, x.responseText));
That's it. It's all asynchronous and concurrent.

How to save an image in a folder in ASP.NET Core MVC that comes from Xamarin.Forms Android?

I'm trying to save an image that comes from Xamarin forms Android but the method Post doesn't get the image, but the image reaches the page.
Xamarin code:
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsPickPhotoSupported)
{
await DisplayAlert("No pick photo", "no pick photo available", "OK");
}
_mediaFile = await CrossMedia.Current.PickPhotoAsync();
if (_mediaFile == null)
return;
lblStatus.Text = _mediaFile.Path;
var content = new MultipartFormDataContent();
content.Add(new StreamContent(_mediaFile.GetStream()), "\"file\"",
$"\"{_mediaFile.Path}\"");
var httpClient = new HttpClient();
var uploadServiceBaseAddress = "http://url/api/Files/Upload";
var httpResponseMessage = await httpClient.PostAsync(uploadServiceBaseAddress, content);
lblStatus.Text = await httpResponseMessage.Content.ReadAsStringAsync();
ASP.NET Core MVC / C# code:
[Route("api/Files/Upload")]
public async Task<string> Post(List<IFormFile> files)
{
try
{
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
var fileName = formFile.FileName.Split('\\').LastOrDefault().Split('/').LastOrDefault();
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "~/Uploads/", fileName);
using (var stream = System.IO.File.Create(filePath))
{
await formFile.CopyToAsync(stream);
}
return "/Uploads/" + fileName;
}
}
}
catch (Exception exception)
{
return exception.Message;
}
return "no files";
}
In my mobile app return "no files", so the image is not saved in the folder.
Does someone know why that is?
Thank you very much!
I found the solution. I leave my code in case it helps someone. I just had to change these lines
[Route("api/Files/Upload")]
public async Task<string> Post()
{
try
{
var fileName = this.Request.Form.Files[0].FileName.Split('\\').LastOrDefault().Split('/').LastOrDefault();
var filePath = Path.Combine(Directory.GetCurrentDirectory(), #"wwwroot\Uploads\", fileName);
using (var stream = System.IO.File.Create(filePath))
{
await this.Request.Form.Files[0].CopyToAsync(stream);
}
return "/Uploads/" + fileName;
}
catch (Exception exception)
{
return exception.Message;
}
return "no files";
}

I can't show a file path when click upload button

I followed the video tutorial Houssem Dellai about Upload File from Xamarin app to ASP.NET server on youtube.
here is the link https://www.youtube.com/watch?v=IVvJX4CoLUY
My problem when i click button upload photo my code is only executed until
var httpResponseMessage = await httpclient.PostAsync(uploadServiceBaseAddress, content);
And didn't show message error, my code cannot executed RemotePathFile.Text for show path file
This is my coding
private async void UploadFile_Clicked(object sender, EventArgs e)
{
var content = new MultipartFormDataContent();
content.Add(new StreamContent(_mediaFile.GetStream()),
"\"file\"",
$"\"{_mediaFile.Path}\"");
var httpclient = new HttpClient();
var uploadServiceBaseAddress = "https://192.168.43.172/api/Files/Upload";
var httpResponseMessage = await httpclient.PostAsync(uploadServiceBaseAddress, content);
RemotePathFile.Text = await httpResponseMessage.Content.ReadAsStringAsync();
}
And This is my UploadController
public class UploadsController : ApiController
{
[Route("api/Files/Upload")]
public async Task<string> Post()
{
try
{
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
var fileName = postedFile.FileName.Split('\\').LastOrDefault().Split('/').LastOrDefault();
var filePath = HttpContext.Current.Server.MapPath("~/Uploads/" + fileName);
postedFile.SaveAs(filePath);
return "/Uploads/" + fileName;
}
}
}
catch (Exception exception)
{
return exception.Message;
}
return "no files";
}
}
Please help me, how i can fix ?

saveStringToLocalFile in WindowsPhone 8.1 in Universal 8.1 App C#

I have this Function:
public async static Task<JsonObject> GetObject(string api)
{
try
{
JsonObject jsonObject = new JsonObject();
if (!IsInternetConnected())
{
string x = await DataBase.readStringFromLocalFile(api + ".txt");
jsonObject = JsonObject.Parse(x);
}
else
{
string x = "";
XDocument doc = XDocument.Load("DataLibrary/apis.xml");
var items = doc.Descendants("string");
foreach (XElement item in items)
{
if (item.Attribute("name").Value == api)
{
HttpClient webclient = new HttpClient();
HttpResponseMessage resp = webclient.PostAsync(new Uri(item.Value.ToString()), null).Result; //here
x = await resp.Content.ReadAsStringAsync();
try
{
await DataBase.saveStringToLocalFile(api + ".txt", x);
Debug.WriteLine("after writing");
}
catch (Exception ex)
{
throw ex;
}
Debug.WriteLine("after after writing");
jsonObject = JsonObject.Parse(x);
break;
}
}
}
return jsonObject;
}
catch (Exception)
{
MainPage.ShowCustomMessage(request_error_allert);
return new JsonObject();
}
}
And this is the saveStringToFile Function that I took from this article: https://pumpkinszwan.wordpress.com/2014/10/27/read-and-write-text-files-windows-8-1-phone-8-1-universal-apps/
public static async Task saveStringToLocalFile(string filename, string content)
{
// saves the string 'content' to a file 'filename' in the app's local storage folder
byte[] fileBytes = System.Text.Encoding.UTF8.GetBytes(content.ToCharArray());
Debug.WriteLine("1");
// create a file with the given filename in the local folder; replace any existing file with the same name
StorageFile newFile = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
Debug.WriteLine("2");
// write the char array created from the content string into the file
using (var stream = await newFile.OpenStreamForWriteAsync())
{
await stream.WriteAsync(fileBytes, 0, fileBytes.Length);
Debug.WriteLine("3");
}
Debug.WriteLine("4");
}
I am calling GetObject 2 times with 2 different APIs.
When the code reaches the saveStringToFile Function, it crashes:
sometimes it crashes inside the saveStringTofile after
Debug.WriteLine("1");
sometimes after Debug.WriteLine("2");
sometimes after Debug.WriteLine("3");
sometimes after Debug.WriteLine("after writing");
sometimes it works the first time with the first API and crashes
with the second.
Its behaviour is really weird, I am trying to catch it with every breakpoint but it crashes randomly every single time. Anything I am missing?

return filename after save in WebAPi

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

Categories

Resources