I am trying to open multiple browsers in parallel, but I can not navigate to the website in more than one window..
Here is how I do it:
namespace XXX
{
public class CoreDriver
{
public IWebDriver driver;
public int my_port { get; set; }
public void Initialize()
{
string chromeee = "";
if (my_port == 50147) { chromeee = "C:/Users/AA/Downloads/chromedriver1/"; }
else if (my_port == 50148) {chromeee = "C:/Users/AA/Downloads/chromedriver2/"; }
else if (my_port == 50149) { chromeee = "C:/Users/AA/Downloads/chromedriver3/"; }
else if (my_port == 50140) { chromeee = "C:/Users/AA/Downloads/chromedriver4/"; }
ChromeOptions options = new ChromeOptions();
options.AddArgument("user-data-dir=C:\\Users\\AA\\AppData\\Local\\Google\\Chrome\\User Data");
var driverService = ChromeDriverService.CreateDefaultService(chromeee);
driverService.HideCommandPromptWindow = true;
driverService.Port = my_port;
driver = new ChromeDriver(driverService, options);
driver.Manage().Timeouts().ImplicitlyWait(new TimeSpan(0,0,12));
driver.Manage().Timeouts().SetPageLoadTimeout(TimeSpan.FromSeconds(13));
//driver navigate
}
}
}
calling it as this:
CoreDriver A1 = new CoreDriver();
A1.my_port = 50147;
A1.Initialize();
CoreDriver A2 = new CoreDriver();
A2.my_port = 50148;
A2.Initialize(); // timeout error here
// ...
Unfortunately, after the second window is opened - timeout error is shownn:
A first chance exception of type 'OpenQA.Selenium.WebDriverException'
occurred in WebDriver.dll
Additional information: The HTTP request to the remote WebDriver
server for URL http:/loca1host:50148/session timed out after 60
seconds.
at this line:
driver = new ChromeDriver(driverService, options);
after rerunning the test with different parameters I have found out that the error is shown due to the specified Chrome profile:
options.AddArgument("user-data-dir=C:\\Users\\AA\\AppData\\Local\\Google\\Chrome\\User
Data");
If I remove the line - then all of my cookies will not be used in ChromeDriver instance and that is not something that I can live with :)
Is there a way to use the same chrome profile in multiple chromedriver instances?
Okay, so I am using my approach as stated above.
My requirements were:
I must keep the cookies of the main chrome profile
I must keep extensions of the main profile
I do not need the history, opened tabs, session etc. of the main profile
after a new start of an existing custom profile - i start it clear without opened tabs
Here is the logic in few words.
First I specify a directory for the existing Google Chrome profile.
If we need to create cookies (i.e. login into some website) then we do it on the main profile of google chrome.
After it is done, close the chrome. Some websites keep cookies for a long time, some - not. So it is in our interest to relogin on the main profile when necessary. Do not keep the Original chrome opened! Otherwise ChromeDriver will throw some warnings.
Next, my script will copy the necessary folders and files into new folder. This folder is our new profile with all cookies. Everything is about 30 megabytes in size on my PC.
If the folder for the new profile already exists - then the program will only copy cookies files. That's shouldn't be more than 1-2 megs of data.
And here is the code. You might want to tweak one thing or another.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
using OpenQA.Selenium.Internal;
using OpenQA.Selenium.Remote;
using System.IO;
using System.Drawing.Imaging;
using System.Management;
using System.Text.RegularExpressions;
using System.Threading;
using System.Diagnostics;
using System.Reflection;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Net;
namespace NAMESPACE
{
public class CoreDriver
{
public IWebDriver driver;
public string my_name { get; set; }
public int my_port { get; set; }
public string default_profile_dir = #"C:\Users\USERNAME\AppData\Local\Google\Chrome\";
public string chromedriver_path = #"C:\Users\USERNAME\Downloads\chromedriver_win32\";
public string site_profile_path;
public string site_profile_path_s;
public string default_path;
public void Initialize()
{
ChromeOptions options = new ChromeOptions();
options.AddArgument("--log-level=3");
options.AddArgument("--test-type");
options.AddArgument("--silent");
options.AddArgument("user-data-dir=" + site_profile_path_s);
options.AddArgument("--disable-plugins"); // disable flash
var driverService = ChromeDriverService.CreateDefaultService(chromedriver_path);
driverService.HideCommandPromptWindow = true;
driverService.Port = my_port;
driver = new ChromeDriver(driverService, options);
driver.Manage().Timeouts().ImplicitlyWait(new TimeSpan(0, 0, 14));
driver.Manage().Timeouts().SetPageLoadTimeout(TimeSpan.FromSeconds(15));
IJavaScriptExecutor jscript = driver as IJavaScriptExecutor;
jscript.ExecuteScript("return window.stop");
}
public void ConfigureProfile()
{
site_profile_path_s = default_profile_dir + "profile " + my_name;
site_profile_path = site_profile_path_s + #"\Default";
default_path = default_profile_dir + #"User Data\Default";
if (!Directory.Exists(site_profile_path))
{
CreateBlankProfile();
}
else
{
// copy existing chrome profile. Keep cache, extensions, etc.
CopyProfileFiles();
// but stay away from opened tabs
RemoveOpenedTabsFiles();
}
}
public void CleanUpOldProfiles()
{
DirectoryInfo di = new DirectoryInfo(default_profile_dir);
DirectoryInfo[] directories = di.GetDirectories("profile*", SearchOption.TopDirectoryOnly);
if (directories.Count() > 0)
{
foreach (var folder in directories)
{
try
{
Directory.Delete(folder.FullName, true);
}
catch
{
}
}
}
}
public void CreateBlankProfile()
{
// new profile direftory
CreateIfMissing();
// copy existing chrome profile. Keep cache, extensions, etc.
// but stay away from opened tabs
CopyProfileFiles();
CopyProfileFolders();
}
public void CopyProfileFiles()
{
// default profile location
DirectoryInfo di = new DirectoryInfo(default_path);
// copy files
List<string> file_lib = new List<string>() { "Cookies", "Login", "Preferences", "Secur" };
FileInfo[] files = di.GetFiles("*", SearchOption.TopDirectoryOnly);
if (files.Count() > 0)
{
foreach (var file in files)
{
if (PassFileOrFolder(file.Name, file_lib))
{
file.CopyTo(site_profile_path + #"\" + file.Name, true);
}
}
}
}
public void RemoveOpenedTabsFiles()
{
// default profile location
DirectoryInfo di = new DirectoryInfo(site_profile_path);
// copy files
List<string> file_lib = new List<string>() { "Current", "Last" };
FileInfo[] files = di.GetFiles("*", SearchOption.TopDirectoryOnly);
if (files.Count() > 0)
{
foreach (var file in files)
{
if (PassFileOrFolder(file.Name, file_lib))
{
File.Delete(file.FullName);
}
}
}
}
public void CopyProfileFolders()
{
// default profile location
DirectoryInfo di = new DirectoryInfo(default_path);
// copy folders
List<string> folder_lib = new List<string>() { "databases", "Extension", " Storage", "Web Applications", "File System", "IndexedDB" };
DirectoryInfo[] directories = di.GetDirectories("*", SearchOption.TopDirectoryOnly);
if (directories.Count() > 0)
{
foreach (var folder in directories)
{
if (PassFileOrFolder(folder.Name, folder_lib))
{
DirectoryCopy(folder.FullName, site_profile_path + #"\" + folder.Name, true);
}
}
}
}
private void CreateIfMissing()
{
Directory.CreateDirectory(site_profile_path);
}
private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
{
// Get the subdirectories for the specified directory.
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
DirectoryInfo[] dirs = dir.GetDirectories();
if (!dir.Exists)
{
throw new DirectoryNotFoundException(
"Source directory does not exist or could not be found: "
+ sourceDirName);
}
// If the destination directory doesn't exist, create it.
if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}
// Get the files in the directory and copy them to the new location.
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string temppath = Path.Combine(destDirName, file.Name);
file.CopyTo(temppath, false);
}
// If copying subdirectories, copy them and their contents to new location.
if (copySubDirs)
{
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
DirectoryCopy(subdir.FullName, temppath, copySubDirs);
}
}
}
public bool PassFileOrFolder(string input, List<string> library)
{
foreach (string name in library)
{
if (input.Contains(name))
{
return true;
}
}
return false;
}
}
}
Please note that I have also implemented a method to clean up all profiles CleanUpOldProfiles
Review the code, make changes to directories etc. After done - make a following call:
CoreDriver something = new CoreDriver(); // creating an object
// settings
something.my_port = 50150; // multiple chrome instances - will be run on different ports
// I am currently having 4 chrome profiles ;)
something.my_name = "mynewprofile"; // full profile name will be: 'profile + my_name'. Check the code of the object.
// void
something.ConfigureProfile(); // creating new profile or updating existing one, if folder eists
something.Initialize(); // starting the browser
sorry for a long answer. Hope it helps you guys somehow :)
Related
I am working on Telegram bot on Mac with C# .netcore console application.
when I created a directory and tried to store files in folder, I have faced with access denied error.
I have tried to set access manually to folders in Finder, but nothing have changed.
here is my FileHelper class to create directories and files:
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
namespace TelegramBotUploader.Core
{
public static class FileHelper
{
public static bool CreateDirectory(string directoryPath)
{
try
{
if (Directory.Exists(directoryPath))
{
return true;
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
DirectorySecurity directorySecurity = new DirectorySecurity();
directorySecurity.AddAccessRule(new FileSystemAccessRule("Everyone", FileSystemRights.FullControl, AccessControlType.Allow));
DirectoryInfo dirInfo = Directory.CreateDirectory(directoryPath);
dirInfo.SetAccessControl(directorySecurity);
}
else
{
var unixDirectoryInfo = new Mono.Unix.UnixDirectoryInfo(directoryPath);
unixDirectoryInfo.CanAccess(Mono.Unix.Native.AccessModes.W_OK);
unixDirectoryInfo.CanAccess(Mono.Unix.Native.AccessModes.X_OK);
unixDirectoryInfo.CanAccess(Mono.Unix.Native.AccessModes.R_OK);
unixDirectoryInfo.Create(Mono.Unix.FileAccessPermissions.AllPermissions);
}
return true;
}
catch(Exception exc)
{
return false;
}
}
public static string GetUserDirectory(string username)
{
return $"./user_files/{username}";
}
}
}
and here is file creating
var fileId = update.Message.Photo.Last().FileId;
var fileInfo = await botClient.GetFileAsync(fileId);
var filePath = fileInfo.FilePath;
string downloadPath = FileHelper.GetUserDirectory(messageSender);
await using FileStream fileStream = System.IO.File.OpenWrite(downloadPath);
await botClient.DownloadFileAsync(filePath: filePath, destination: fileStream);
qualifiedUploaders.Remove(messageSender);
i wrote an application which is a custom console that allows execution of various commands. One of the commands allows to find a file's full path, according to part of its name. The input data is a string, which equals to part\full name of the file.
My question is - how to minimize the search code runtime complexity as much as possible?
Here is the command's code:
using CustomConsole.Common;
using System;
using System.Collections.Generic;
using System.IO;
namespace Shell_Commander.Commands
{
class FindFileCommand : ICommand
{
private string _findFileCommandName = "findfile";
public string Name { get { return _findFileCommandName; } set { _findFileCommandName = value; } }
public string Execute(string parameters)
{
var fileLocations = new Dictionary<string, bool>();
try
{
var splittedParameters = parameters.Split(" ");
var initialLocation = splittedParameters[0];
var fileName = splittedParameters[1];
foreach (var filePath in Directory.GetFiles(initialLocation, "*.*", SearchOption.AllDirectories))
{
fileLocations.Add(filePath, false);
if (Path.GetFileName(filePath) == fileName || Path.GetFileNameWithoutExtension(filePath) == fileName)
{
fileLocations[filePath] = true;
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
bool fileFound = false;
string returnedOutput = "";
foreach (var location in fileLocations.Keys)
{
if (fileLocations[location])
{
returnedOutput += $"The file found in path: {location}\n";
Console.Write(returnedOutput);
fileFound = true;
}
}
if (!fileFound)
{
returnedOutput = "The file not found in this path";
Console.WriteLine(returnedOutput);
return returnedOutput;
}
return returnedOutput;
}
}
}
Example - for the input parameters "c:\temp test", the output can be:
The file found in path: c:\temp\test.json
The file found in path: c:\temp\test.json
The file found in path: c:\temp\test.xml
The file found in path: c:\temp\test.json
The file found in path: c:\temp\test.xml
The file found in path: c:\temp\test\test.json
You can simply your foreach like this
var fileLocations = Directory.GetFiles(initialLocation, $"{filePath}.*", SearchOption.AllDirectories);
foreach (var location in fileLocations)
{
returnedOutput += $"The file found in path: {location}\n";
Console.Write(returnedOutput);
}
The rest of the code also can be simplified.
I want to create file inside a specific folder in google drive ( not the default location ) using Xamarin.Andriod
I'm using the below code
MetadataChangeSet changeSetfile = new MetadataChangeSet.Builder()
.SetTitle("Test.jpg")
.SetMimeType("image/jpeg")
.Build();
DriveClass.DriveApi
.GetRootFolder(_googleApiClient)
.CreateFile(_googleApiClient, changeSetfile, contentResults.DriveContents);
Implement GoogleApiClient.IConnectionCallbacks
Obtain a GoogleApiClient with DriveClass.API and DriveClass.ScopeFile
GoogleApiClient Example:
if (_googleApiClient == null) // _googleApiClient is a class level variable
{
_googleApiClient = new GoogleApiClient.Builder(this)
.AddApi(DriveClass.API)
.AddScope(DriveClass.ScopeFile)
.AddConnectionCallbacks(this)
.AddOnConnectionFailedListener(onConnectionFailed)
.Build();
}
if (!_googleApiClient.IsConnected)
_googleApiClient.Connect();
Once connected, Query for folder, create if needed and then "write" a file to it.
Folder and File Example:
var folderName = "StackOverflow";
using (var driveId = DriveClass.DriveApi.GetRootFolder(_googleApiClient))
using (var query = new QueryClass.Builder().AddFilter(Filters.And(Filters.Eq(SearchableField.Title, folderName), Filters.Eq(SearchableField.Trashed, false))).Build())
using (var metaBufferResult = await driveId.QueryChildrenAsync(_googleApiClient, query))
{
if (metaBufferResult.Status.IsSuccess)
{
DriveId folderId = null;
foreach (var metaData in metaBufferResult.MetadataBuffer)
{
if (metaData.IsFolder && metaData.Title == folderName)
{
folderId = metaData.DriveId;
break;
}
}
IDriveFolder driveFolder = null;
switch (folderId)
{
case null: // if folder not found, create it and fall through to default
using (var folderChangeSet = new MetadataChangeSet.Builder().SetTitle(folderName).Build())
using (var folderResult = await driveId.CreateFolderAsync(_googleApiClient, folderChangeSet))
{
if (!folderResult.Status.IsSuccess)
{
Log.Error(TAG, folderResult.Status.StatusMessage);
break;
}
driveFolder = folderResult.DriveFolder;
}
goto default;
default:
driveFolder = driveFolder ?? folderId.AsDriveFolder();
// create your file in the IDriveFolder obtained,
using (var contentResults = await DriveClass.DriveApi.NewDriveContentsAsync(_googleApiClient))
{
if (contentResults.Status.IsSuccess)
{
using (var writer = new OutputStreamWriter(contentResults.DriveContents.OutputStream))
{
writer.Write("StackOverflow Rocks");
using (var changeSet = new MetadataChangeSet.Builder()
.SetTitle("StackOverflow Rocks")
.SetStarred(true)
.SetMimeType("text/plain")
.Build())
using (var driveFileResult = await driveFolder.CreateFileAsync(_googleApiClient, changeSet, contentResults.DriveContents))
{
if (driveFileResult.Status.IsSuccess)
Log.Debug(TAG, "File created, open https://drive.google.com to review it");
else
Log.Error(TAG, driveFileResult.Status.StatusMessage);
}
}
}
}
driveFolder.Dispose();
break;
}
folderId?.Dispose();
}
else
{
Log.Error(TAG, metaBufferResult.Status.StatusMessage);
}
}
Notes:
Do this on a background thread
Drive allows multiple files/folders with same name (Title)
Query for existing files if you want to replace one
Query for existing folders unless you really what multiple folders with the same Title
Folders and files in the Trash are returned queries unless excluded.
Make use of Using blocks and Dispose to avoid leaks
I have an application that displays a treeview of a folders, with the format below:
- Main Folder
- SubFolder
- SubFolder
- SubFolder
- SubFolder
Questions:
1) How do I search for all the folders (not files) and add them to a List called syncDirectories(FIXED)
2) How would I then iterate through my JSON object and save it back to List<SavedData> in order to add/remove data (FIXED)
3) How would I loop through my syncDirectories List and add all the nodes to the TreeView in WPF. (Keep in mind the treeview with checkbox class i am using.
And so on. Also, this is an example to demonstrate the format, and users could have more subdirectories.
Each of these folders has a checkbox on it thanks to this TREEVIEW CLASS.
In order to save all the folders and subfolders the user adds (and checks), I thought saving a JSON file listing all the folder and subfolder paths, along with their (true/false) checked values would be a good idea. Right? I am using Json.NET.
Messing around with some JSON, I came up with this format:
{
"path": "path/here",
"subDirectories": [
{"path": "sub/path/here","sync": false},
{"path": "sub/path/here","sync": true},
{"path": "sub/path/here","sync": false}
]
}
Which translated into the class:
public class SavedData
{
public string path { get; set; }
public List<SubDirectory> subDirectories { get; set; }
}
public class SubDirectory
{
public string path { get; set; }
public bool sync { get; set; }
public List<SubDirectory> subDirectories { get; set; }
}
This raises the question, How do I search for all the folders (not files) and add them to a List called syncDirectories?
Here is what I have so far in my addDirectory function:
private void addDirectory()
{
var dialog = new FolderBrowserDialog();
DialogResult result = dialog.ShowDialog();
if (result == System.Windows.Forms.DialogResult.OK)
{
SavedData data = new SavedData();
data.path = dialog.SelectedPath;
syncedDirectories.Add();
}
// Add the data to syncedDirectories List, then save the list and refresh the treeView
saveData();
}
And the save function:
public void saveData()
{
JsonSerializer serializer = new JsonSerializer();
using (StreamWriter sw = new StreamWriter(appData))
using (JsonWriter writer = new JsonTextWriter(sw))
{
serializer.Serialize(writer, syncedDirectories);
}
}
When I save the data into a file, How would I then iterate through my JSON object and save it back to List<SavedData> in order to add/remove data?
UPDATE 1
I have figured out how to handle my data (Below), but how exactly would i add these "nodes" to the treeview?
public void getDirectories(string dir)
{
// Create the Data Object
SavedData data = new SavedData();
data.path = dir;
data.subDirectories = new List<SubDirectory>();
foreach (string directory in Directory.GetDirectories(dir))
{
SubDirectory subDir = new SubDirectory();
subDir.path = directory;
subDir.subDirectories = getSubDir(directory);
data.subDirectories.Add(subDir);
}
syncedDirectories.Add(data);
}
private List<SubDirectory> getSubDir(string dir)
{
List<SubDirectory> dataList = new List<SubDirectory>();
SubDirectory subDir = new SubDirectory();
foreach (string directory in Directory.GetDirectories(dir))
{
subDir.path = directory;
subDir.subDirectories = new List<SubDirectory>();
subDir.subDirectories = getSubDir(directory);
dataList.Add(subDir);
}
return dataList;
}
Later, i plan to release this program open source on GitHub. When i do, i will post the link here.
After a couple hours of playing around with a function, I realized two functions are needed to complete this task.
public void getDirectories(string dir)
{
// Create the Data Object
SavedData data = new SavedData();
data.path = dir;
data.subDirectories = new List<SubDirectory>();
foreach (string directory in Directory.GetDirectories(dir))
{
SubDirectory subDir = new SubDirectory();
subDir.path = directory;
subDir.subDirectories = getSubDir(directory);
data.subDirectories.Add(subDir);
}
syncedDirectories.Add(data);
}
private List<SubDirectory> getSubDir(string dir)
{
List<SubDirectory> dataList = new List<SubDirectory>();
SubDirectory subDir = new SubDirectory();
foreach (string directory in Directory.GetDirectories(dir))
{
subDir.path = directory;
subDir.subDirectories = new List<SubDirectory>();
subDir.subDirectories = getSubDir(directory);
dataList.Add(subDir);
}
return dataList;
}
This gets the main directory then loops and finds all the subdirectories and adds them to my list.
Description
Download multiple files using webclient's DownloadFileAsync and utilizing a text file for URL input for download.
Problem
The approach that I have used won't download files at all. Just runs and does nothing. It fills the list array then quits the program without downloading a single file. I have googled for solutions but come up shorthanded. Then attempted to search for a solution in the database here with same results. Any help is appreciated.
Questions
Why does this approach not work?
What can I do to improve this and learn from this.
Code
DownloadClass.cs
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using System.Windows.Forms;
namespace ThreadTest
{
class DownloadClass
{
public struct download
{
public static string URL { get; set; }
public static string file { get; set; }
public static string[] link;
public static int downloadcount;
}
public static List<string> list = new List<string>();
public static WebClient wc = new WebClient();
public static void Download()
{
int count = 0;
download.URL = list[0];
Uri URI = new Uri(download.URL);
UriBuilder uri = new UriBuilder(URI);
download.link = uri.Path.ToLower().Split(new char[] { '/' });
count = 0;
// Find file
foreach (string abs in download.link)
{
count++;
if (abs.ToLower().Contains(".html") || abs.ToLower().Contains(".exe") || abs.ToLower().Contains(".txt"))
{
try
{
download.file = download.link[count];
wc.Proxy = null;
wc.DownloadFileCompleted += new AsyncCompletedEventHandler(wc_DownloadFileCompleted);
wc.DownloadFileAsync(URI, Application.StartupPath + "\\" + download.file);
break;
}
catch (Exception)
{ }
}
}
}
public static void BeginDownload()
{
new Thread(Download).Start();
}
public static void wc_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
int count = 0;
download.downloadcount++;
download.URL = list[0];
Uri URI = new Uri(download.URL);
UriBuilder uri = new UriBuilder(URI);
download.link = uri.Path.ToLower().Split(new char[] { '/' });
count = 0;
// Find file
foreach (string abs in download.link)
{
count++;
if (abs.ToLower().Contains(".html") || abs.ToLower().Contains(".exe") || abs.ToLower().Contains(".txt"))
{
try
{
download.file = download.link[count];
}
catch (Exception)
{ }
}
}
list.RemoveAt(0);
if (list.Count > 0)
{
wc.DownloadFileAsync(URI, list[download.downloadcount], Application.StartupPath + "\\" + download.file);
}
else
{
Console.WriteLine("Downloading is done.");
Environment.Exit(0);
}
}
}
}
Program.cs (Main Class)
using System;
using System.IO;
using System.Collections.Generic;
using System.Windows.Forms;
namespace ThreadTest
{
class Program
{
static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Usage: {0} <download txtfile>", Environment.GetCommandLineArgs()[0]);
Environment.Exit(0);
}
int counter = 0;
string line;
string format = string.Format("{0}\\{1}", Application.StartupPath, args[0]);
// Read the file line by line.
using(StreamReader file = new StreamReader(format))
{
while ((line = file.ReadLine())!= null)
{
// Store urls in a list.
DownloadClass.list.Add(line);
counter++;
}
}
DownloadClass.BeginDownload();
}
}
}
Besides being bad design there are lots of issues that lead to your code not (or nor correctly working).
You need to make sure that you application lives while it downloads something. Your current app quits right away (you have to wait for the downloading to complete in your main).
You application may download the same file multiple times but not download others at all (You need to completely lock object when they are used in an async=multithreading way like here when accessing static objects) BTW: Don't use static objects at all to avoid that in the first place.
Even if 2 is corrected it may still download the same file multiple times into the same filename and thus fail.
As long as you have no knowledge about multithreading I'd recommend you use the synchoneous methods to avoid all those problems.