How to add a completed event when using httpclient to download files? - c#

private async Task Download()
{
FilesDownoads downloads = new FilesDownloads(#"d:\testfiles");
downloads.PrepareLinks();
using var client = new HttpClient();
for (int i = 0; i < files.Count; i++)
{
try
{
using var s = await client.GetStreamAsync(files[i]);
using var fs = new FileStream(#"e:\files1\file" + i.ToString() + ".txt", FileMode.OpenOrCreate);
await s.CopyToAsync(fs);
}
catch { }
}
}
private async void button1_Click(object sender, EventArgs e)
{
await Download();
}
the goal is to download each file and when the download is completed for each file to do something with the file in the completed event.
i tried to google but still not sure how to add the progress and completed events (mostly the completed ).

Related

How can I use sync task await to test downloading?

In form1 button click event :
First I'm trying to generate the Radar links.
private async void btnStart_Click(object sender, EventArgs e)
{
lblStatus.Text = "Downloading...";
await rad.GetRadarImages();
await sat.DownloadSatelliteAsync();
foreach(string link in sat.SatelliteUrls())
{
urls.Add(link);
}
await DownloadAsync();
}
The Radar class :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Extract
{
class Radar
{
private List<string> links = new List<string>();
string defaultlink;
DateTime current;
string currentLink;
public List<DateTime> dates = new List<DateTime>();
DateTime workingFirstDateTime;
public async Task GetRadarImages()
{
defaultlink = "mysite.com/Radar_";
current = RoundDown(DateTime.Now, TimeSpan.FromMinutes(-5));
var ct = current.ToString("yyyyMMddHHmm");
currentLink = defaultlink + ct + ".gif";
using (System.Net.WebClient wc = new System.Net.WebClient())
{
wc.DownloadFileCompleted += (s, e) =>
{
if (e.Error != null)
{
current = current.AddMinutes(-5);
ct = current.ToString("yyyyMMddHHmm");
currentLink = defaultlink + ct + ".gif";
wc.DownloadFileTaskAsync(new Uri(currentLink), #"d:\Downloaded Images\Radar\radar0.gif");
}
else
{
workingFirstDateTime = current;
GenerateRadarLinks();
}
};
await wc.DownloadFileTaskAsync(new Uri(currentLink), #"d:\Downloaded Images\Radar\radar0.gif");
}
}
private void GenerateRadarLinks()
{
for (var i = 0; i < 34; i++)
{
current = current.AddMinutes(-5);
dates.Add(current);
var date = dates[i].ToString("yyyyMMddHHmm");
links.Add(defaultlink + date + ".gif");
}
dates.Insert(0, workingFirstDateTime);
}
DateTime RoundDown(DateTime date, TimeSpan interval)
{
return new DateTime(date.Ticks / interval.Ticks *
interval.Ticks);
}
}
}
I'm downloading the first time :
await wc.DownloadFileTaskAsync(new Uri(currentLink), #"d:\Downloaded Images\Radar\radar0.gif");
Then in the completed event I'm checking if the downloaded file was fine or not by checking for error/s :
if (e.Error != null)
{
current = current.AddMinutes(-5);
ct = current.ToString("yyyyMMddHHmm");
currentLink = defaultlink + ct + ".gif";
wc.DownloadFileTaskAsync(new Uri(currentLink), #"d:\Downloaded Images\Radar\radar0.gif");
}
else
{
workingFirstDateTime = current;
GenerateRadarLinks();
}
};
If there is an error rebuild the link and try to download again this way I'm trying to rebuild the link over and over trying to download it until the download is success if there is no error/s the download is success then generate the link by calling the method GenerateRadarLinks()
The problem is I used a breakpoint on the line :
if (e.Error != null)
I see error 404 not found so the link is incorrect it should try to download over again with the new built link but instead it's jumping to the Program.cs and throw there the exception 404 not found :
Why it's not trying to download the new built link over and over again until success ?

File used by another process - StreamWriter

I have checked all other solutions, nothing is working.
I am calling an asynchronous logging method from different button events.
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() => LoggerTest());
}
private async void button2_Click(object sender, EventArgs e)
{
await Task.Run(() => LoggerTest());
}
private async void LoggerTest()
{
for (int i = 0; i < 10000; i++)
{
Logger.Log(string.Format("Counter: {0}", i));
Thread.Sleep(10);
}
}
Log method uses StreamWriter
private void Log(string log)
{
if (!IsFileLocked(fullPath))
{
using (StreamWriter file = new StreamWriter(fullPath, append: true))
{
file.WriteLine(log);
file.Close(); // I know this is unnecessary in the using block
}
}
}
private bool IsFileLocked(string file)
{
try
{
using (var stream = File.OpenRead(file))
return false;
}
catch (IOException)
{
return true;
}
}
When I click on button1 and button2, the below exception is caught:
System.IO.IOException: 'The process cannot access the file 'C:..\x.txt' because it is being used by another process.'
try something like this
private static readonly object locker = new object();
lock (locker)
{
using (FileStream fileStream = new FileStream("FilePath"), FileMode.Append))
{
using (StreamWriter writer = new StreamWriter(fileStream))
{
writer.WriteLine(log);
}
}
}
lock keyword will lock the stream writer till the current writer process is finished

wait for event handler of a single thread to execute

I am uploading files to S3. But the file upload fails silently without any error. So I have written an event handler to keep track of the upload.
Below is my function for file upload.
I want to wait for event handler till it completes its execution. Please note it is synchronous file upload.
public async Task<ActionResult> UploadBatchDocuments(UploadViewModel model)
{
TransferUtility transfer = new TransferUtility(client);
foreach (var file in model.Documents)
{
TransferUtilityUploadRequest request = new TransferUtilityUploadRequest()
{
BucketName = bucketName,
CannedACL = S3CannedACL.PublicRead,
Key = string.Format(file.FilePath + "/{0}", fileName),
InputStream = file.File.InputStream,
};
await Task.Run(()=>request.UploadProgressEvent += uploadRequest_UploadPartProgressEvent);
await Task.Run(() => transfer.Upload(request));
}
}
Below is my event handler which runs for every file upload,
public async void uploadRequest_UploadPartProgressEvent(object sender, UploadProgressArgs e)
{
if (e.PercentDone == 100)
{
var subs = ((Amazon.S3.Transfer.TransferUtilityUploadRequest)sender).Key.Split('/');
var fileName = subs[subs.Length - 1];
for (int i = 0; i < tempModel.Documents.Count; i++)
{
if (tempModel.Documents[i].File.FileName.Equals(fileName))
{
tempModel.Documents[i].isUploaded = true;
}
}
}
}
My problem is:Control keeps switching between UploadBatchDocuments and uploadRequest_UploadPartProgressEvent(which is an event handler).
Required:I want the control to wait in UploadBatchDocuments to wait for the thread to complete in uploadRequest_UploadPartProgressEvent

Functions aren't finishing before starting the next

I have a youtube uploader, and I am generating a video from an audio file, which works fine, but when I am uploading to Youtube the program still runs when I am trying to wait for it to finish uploading before repeating
Here I generate a video:
private void button2_Click(object sender, EventArgs e)
{
if (status.Text == "Stopped")
{
if (!generatearticle.IsBusy)
{
// started
status.Text = "Started";
status.ForeColor = System.Drawing.Color.Green;
start.Text = "Stop Generating";
generatearticle.RunWorkerAsync();
}
}
else
{
if(generatearticle.IsBusy)
{
generatearticle.CancelAsync();
// started
status.Text = "Stopped";
status.ForeColor = System.Drawing.Color.Red;
start.Text = "Start Generating";
}
}
}
private void core()
{
// generate audio
int i = 0;
for (int n = 1; n < co; n++)
{
// generate video and upload to
// youtube, this generates, but
// when uploading to youtube this for
// loop carries on when I want it to
// upload to youtube first before carrying on
generatevideo(image, articlename);
}
}
private void generateVideo(string images, String articlename)
{
//generate the video here, once done upload
{code removed, this just generates a video, nothing important}
// now upload (but I want it to finish before repeating the core() function
try
{
new UploadVideo().Run(articlename, file);
}
catch (AggregateException ex)
{
foreach (var e in ex.InnerExceptions)
{
ThreadSafe(() =>
{
this.Invoke((MethodInvoker)delegate
{
status.Text = e.Message;
status.ForeColor = System.Drawing.Color.Red;
});
});
}
}
}
How I am uploading to Youtube:
using System;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Upload;
using Google.Apis.Util.Store;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
namespace articletoyoutube
{
/// <summary>
/// YouTube Data API v3 sample: upload a video.
/// Relies on the Google APIs Client Library for .NET, v1.7.0 or higher.
/// See https://code.google.com/p/google-api-dotnet-client/wiki/GettingStarted
/// </summary>
class UploadVideo
{
// to access form controlls
Form1 core = new Form1();
public async Task Run(string articlename, string filelocation)
{
UserCredential credential;
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
// This OAuth 2.0 access scope allows an application to upload files to the
// authenticated user's YouTube channel, but doesn't allow other types of access.
new[] {
YouTubeService.Scope.YoutubeUpload
},
"user",
CancellationToken.None
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
});
var video = new Video();
video.Snippet = new VideoSnippet();
video.Snippet.Title = articlename;
video.Snippet.Description = "News story regarding" + articlename;
video.Snippet.Tags = new string[] {
"news",
"breaking",
"important"
};
video.Snippet.CategoryId = "25"; // See https://developers.google.com/youtube/v3/docs/videoCategories/list
video.Status = new VideoStatus();
video.Status.PrivacyStatus = "public"; // or "private" or "public"
var filePath = filelocation; // Replace with path to actual movie file.
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", fileStream, "video/*");
videosInsertRequest.ProgressChanged += videosInsertRequest_ProgressChanged;
videosInsertRequest.ResponseReceived += videosInsertRequest_ResponseReceived;
await videosInsertRequest.UploadAsync();
}
}
void videosInsertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
{
switch (progress.Status)
{
case UploadStatus.Uploading:
core.prog_up.Text = "{0} bytes sent." + progress.BytesSent;
break;
case UploadStatus.Failed:
core.status.Text = "An error prevented the upload from completing.\n{0}" + progress.Exception;
core.status.ForeColor = System.Drawing.Color.Red;
break;
}
}
void videosInsertRequest_ResponseReceived(Video video)
{
core.prog_up.Text = "Video id '{0}' was successfully uploaded." + video.Id;
}
}
}
The background worker just runs core();
When it reaches the function
new UploadVideo().Run(articlename, file);
It starts uploading but starts repeating the core function again thus generating another video before that video has uploaded.... If I use
new UploadVideo().Run(articlename, file).Wait();
Then the program just stops and waits indefintly until I close the program, how can I wait for the Upload class/method to finish before carrying on with the fore loop in the core method?
To the guy who answered, when I add await before the new Upload... it gives me:
Severity Code Description Project File Line Suppression State
Error CS4033 The 'await' operator can only be used within an async
method. Consider marking this method with the 'async' modifier and
changing its return type to
'Task'. articletoyoutube C:\Users\Laptop\Documents\Visual Studio
2017\Projects\articletoyoutube\articletoyoutube\Form1.cs 254 Active
Make sure the async keyword is used on your methods and use the await keyword for the Tasks.
For example:
private async Task core()
{
// generate audio
int i = 0;
for (int n = 1; n < co; n++)
{
await generatevideo(image, articlename);
}
}
private async Task generateVideo(string images, String articlename)
{
//generate the video here,
try
{
var uploadVideo = new UploadVideo();
await uploadVideo.Run(articlename, file);
}
catch (AggregateException ex)
{
foreach (var e in ex.InnerExceptions)
{
ThreadSafe(() =>
{
this.Invoke((MethodInvoker)delegate
{
status.Text = e.Message;
status.ForeColor = System.Drawing.Color.Red;
});
});
}
}
}
You need to use await all the way up your call stack to where your event handler is, this will require changing many of your methods.
private async Task core()
{
// generate audio
int i = 0;
for (int n = 1; n < co; n++)
{
// generate video and upload to
// youtube, this generates, but
// when uploading to youtube this for
// loop carries on when I want it to
// upload to youtube first before carrying on
await generatevideo(image, articlename);
}
}
private async Task generateVideo(string images, String articlename)
{
//generate the video here, once done upload
{code removed, this just generates a video, nothing important}
// now upload (but I want it to finish before repeating the core() function
try
{
await new UploadVideo().Run(articlename, file);
}
catch (AggregateException ex)
{
foreach (var e in ex.InnerExceptions)
{
ThreadSafe(() =>
{
this.Invoke((MethodInvoker)delegate
{
status.Text = e.Message;
status.ForeColor = System.Drawing.Color.Red;
});
});
}
}
}
Note, using async/await does not work with BackgroundWorker you will need to switch to using Task.Run and a CancellationToken to signal cancellation.
Task _backgroundWork;
CancellationTokenSource _cts;
private void button2_Click(object sender, EventArgs e)
{
if (status.Text == "Stopped")
{
if (!generatearticle.IsBusy)
{
// started
status.Text = "Started";
status.ForeColor = System.Drawing.Color.Green;
start.Text = "Stop Generating";
_cts = new CancellationTokenSource();
_backgroundWork = Task.Run(() => core(_cts.Token), _cts.Token);
}
}
else
{
if(!_backgroundWork.IsCompleted)
{
_cts.Cancel();
// started
status.Text = "Stopped";
status.ForeColor = System.Drawing.Color.Red;
start.Text = "Start Generating";
}
}
}

Multiple asynchronous method calls in a loop Window Phone 7

I have a collection of picture Objects for which I need to download thumbs and pictures files located on dataservise, how can I managed this?
In this method I have loop to call three methods; one to add objects to data base, second to download and save picture thumb and third to download and save picture file the other two is ClientOpenReadCompleted methods.
public bool AddAllPhoto()
{
var amount = App.ViewModel.NewPictures.Count;
for (int i = 0; i < amount; i++)
{
//to add picture to DB
SavePicture(App.ViewModel.NewPictures[i]);
DownloadPicture(NewPictures[i].ID.ToString());
DownloadPictureThumb(NewPictures[i].ID.ToString()));
}
return true;
}
Second;
public void DownloadPictureThumb(string path)
{
string outputString = String.Format("http://" + App.ServerAdress + "/ /Pictures/Thumbs/{0}.jpg", path);
var client = new WebClient();
client.OpenReadCompleted += ClientOpenReadCompleted1;
client.OpenReadAsync(new Uri(outputString));
}
private static void ClientOpenReadCompleted1(object sender, OpenReadCompletedEventArgs e)
{
var resInfo = new StreamResourceInfo(e.Result, null);
var reader = new StreamReader(resInfo.Stream);
byte[] contents;
using (var bReader = new BinaryReader(reader.BaseStream))
{
contents = bReader.ReadBytes((int)reader.BaseStream.Length);
}
var file = IsolatedStorageFile.GetUserStoreForApplication();
var thumbFilePath = String.Format(PicturesThumbsColectionKey + "{0}", PictureDataStoreLocal.ID);
var stream = thumbFile.CreateFile(thumbFilePath);
stream.Write(contents, 0, contents.Length);
stream.Close();
}
And third one
public void DownloadPicture(string path)
{
string outputString = String.Format("http://" + App.ServerAdress + "/Pictures/{0}.jpg", path);
var client = new WebClient();
client.OpenReadCompleted += ClientOpenReadCompleted1;
client.OpenReadAsync(new Uri(outputString));
}
private static void ClientOpenReadCompleted1(object sender, OpenReadCompletedEventArgs e)
{
var resInfo = new StreamResourceInfo(e.Result, null);
var reader = new StreamReader(resInfo.Stream);
byte[] contents;
using (var bReader = new BinaryReader(reader.BaseStream))
{
contents = bReader.ReadBytes((int)reader.BaseStream.Length);
}
var file = IsolatedStorageFile.GetUserStoreForApplication();
IsolatedStorageFileStream stream = file.CreateFile(PictureDataStoreLocal.ID.ToString());
stream.Write(contents, 0, contents.Length);
stream.Close();
}
I assume you want to process the pictures synchronously. If so I would use a wait handle. The easiest way to do this would be to declare a private AutoResetEvent field. The AutoResetEvent is good here because it just lets one thread through and then blocks again automatically.
If you do this you will need to make sure of two things:
1. You do ALL work on a different thread so that when you call WaitOne() you aren't blocking the thread that is supposed to be doing the work.
2. You always reset the wait handle regardless of the outcome of the server calls.
To take care of 1. you just need to update your loop:
m_waitHandle.Reset(); // Make sure the wait handle blocks when WaitOne() is called
for (int i = 0; i < amount; i++)
{
// Process on a background thread
ThreadPool.QueueUserWorkItem((obj) =>
{
// Get the current index. This is an anonymous method so if
// we use 'i' directly we will not necessarily be using the
// correct index. In our case the wait handle avoids this
// problem as the pictures are downloaded one after the other
// but it's still good practise to NEVER use a loop variable in
// an anonymous method.
int index = (int)obj;
//to add picture to DB
SavePicture(App.ViewModel.NewPictures[index]);
DownloadPicture(NewPictures[index].ID.ToString());
DownloadPictureThumb(NewPictures[index].ID.ToString()));
}, i);
m_waitHandle.WaitOne(); // Wait for processing to finish
}
For 2. you need to make sure that m_waitHandle.Set() is ALWAYS called when processing is finished.
What I do is send extra parameters to the OpenReadCompleted event using a delegate like so,
someimage.LoadingCompleted += delegate(object sender, EventArgs imge) { someimage_LoadingCompleted(sender, imge, _item, "someimage"); };
and then in someimage_LoadingCompleted I have code within a switch statement.
Here is my solution, not that elegant but working one; If you have any suggestion to improve , please post and I will edit my post.
EventWaitHandle m_WaitHandle;
public bool AddAllPhoto()
{
var amount = App.ViewModel.NewPictures.Count;
if (m_WaitHandle!=null)
m_WaitHandle.Reset();
for (int i = 0; i < amount; i++)
{
{
SavePicture(App.ViewModel.NewPictures[i]);
ThreadPool.QueueUserWorkItem((obj) =>
{
var index = (int)obj;
DownloadPictureThumb(App.ViewModel.NewPictures[index].ID.ToString());
DownloadPicture(App.ViewModel.NewPictures[index].ID.ToString());
},i);
if (m_WaitHandle != null) m_WaitHandle.WaitOne();
}
return true;
}
public void DownloadPictureThumb(string path)
{
string outputString = String.Format("http://" + App.ServerAdress + "/Pictures/Thumbs/{0}.jpg", path);
var client = new WebClient();
client.OpenReadCompleted += ClientOpenReadCompleted2;
client.OpenReadAsync(new Uri(outputString),path);
}
private static void ClientOpenReadCompleted2(object sender, OpenReadCompletedEventArgs e)
{
var resInfo = new StreamResourceInfo(e.Result, null);
var reader = new StreamReader(resInfo.Stream);
byte[] contents;
using (var bReader = new BinaryReader(reader.BaseStream))
{
contents = bReader.ReadBytes((int)reader.BaseStream.Length);
}
var file = IsolatedStorageFile.GetUserStoreForApplication();
var thumbFilePath = String.Format(PicturesThumbsColectionKey + "{0}", e.UserState as string);
var stream = file.CreateFile(thumbFilePath);
stream.Write(contents, 0, contents.Length);
stream.Close();
}
public void DownloadPicture(string path)
{
string outputString = String.Format("http://" + App.ServerAdress + "/Pictures/{0}.jpg", path);
var client = new WebClient();
client.OpenReadCompleted += ClientOpenReadCompleted1;
client.OpenReadAsync(new Uri(outputString), path);
}
private static void ClientOpenReadCompleted1(object sender, OpenReadCompletedEventArgs e)
{
var resInfo = new StreamResourceInfo(e.Result, null);
var reader = new StreamReader(resInfo.Stream);
byte[] contents;
using (var bReader = new BinaryReader(reader.BaseStream))
{
contents = bReader.ReadBytes((int)reader.BaseStream.Length);
}
var file = IsolatedStorageFile.GetUserStoreForApplication();
var stream = file.CreateFile(e.UserState as string);
stream.Write(contents, 0, contents.Length);
stream.Close();
}
[Here][1] you will find explanation to how to get the url from WebClient in OpenReadCompleted?

Categories

Resources