In my web application which has some automation process to download the files from the website. To achieve that I used selenium c# chrome driver.
Sample code snippets
public void Download(string name,string pass)
{
try
{
ChromeOptions options = new ChromeOptions();
options.AddArguments("--proxy-server=http://192.168.5.62:8095");
options.AddUserProfilePreference("safebrowsing.enabled", true);
options.AddUserProfilePreference("disable-popup-blocking", "true");
options.AddUserProfilePreference("download.default_directory",#"C:\Temp");
using (var driver = new ChromeDriver(HostingEnvironment.ApplicationPhysicalPath, options)){
//driver.findElement(By.xpath("//a/u[contains(text(),'Re-Submit')]")).click();
driver.FindElementById("save").Click();
}
}
catch (Exception ex)
{
Logger.LogWriter("LAS", ex, "CusDataLogic", "Download");
}
}
above code (not complete code) works fine and save file properly. But I need to rename that file downloading or after download. Have any possible way to rename that file?
Edited: Please don't mark this as a duplicate. I'm asking for C#, not python. I saw that question too. but it not helped to me
watching directory is not always good, because sometime saved filename is different than filename in URL.
go to chrome download page and loop until all download complete, you can see below how to select special element #shadow-root with CSS selector
using (var driver = new ChromeDriver(HostingEnvironment.ApplicationPhysicalPath, options)))
{
//driver.findElement(By.xpath("//a/u[contains(text(),'Re-Submit')]")).click();
driver.FindElementById("save").Click();
// wait 5 second until download started
Thread.Sleep(5000);
// Go to chrome download page
driver.Navigate().GoToUrl("chrome://downloads/");
string oldName = "";
bool downloadcomplete = false;
string cssNames = "downloads-manager /deep/ downloads-item /deep/ [id='name']";
string cssDlProgress = "downloads-manager /deep/ downloads-item /deep/ [class*='show-progress']";
while (!downloadcomplete)
{
var progressElements = driver.FindElements(By.CssSelector(cssDlProgress));
// check until no download progress bar
if (progressElements.Count() == 0)
{
oldName = driver.FindElement(By.CssSelector(cssNames)).Text;
downloadcomplete = true;
}
else
{
// download still in progress, wait.
Thread.Sleep(1000);
}
}
// download complete
// remove downloaded file
driver.FindElement(By.CssSelector("downloads-manager /deep/ downloads-item /deep/ [id='remove']")).Click();
// rename
File.Move(#"C:\Temp\" + oldName, #"C:\Temp\newname.ext");
}
The Snippet Below Will wait Until File downloaded Then return FilePath I Wrote this as an extension method :
public static string GetDonwloadedFileName(this IWebDriver driver)
{
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
js.ExecuteScript("window.open()");
var allWinowHandles = driver.WindowHandles;
foreach (var winHandle in allWinowHandles)
{
//Switch to second window
if (!winHandle.Equals(driver.CurrentWindowHandle))
{
driver.SwitchTo().Window(winHandle);
}
}
// navigate to chrome downloads
driver.Navigate().GoToUrl("chrome://downloads");
IJavaScriptExecutor downloadWindowExecutor = (IJavaScriptExecutor)driver;
// Wait for Download till 100% completion
double percentageProgress = (double)0;
while (percentageProgress != 100)
{
try
{
percentageProgress = (long)downloadWindowExecutor.ExecuteScript("return document.querySelector('downloads-manager').shadowRoot.querySelector('#downloadsList downloads-item').shadowRoot.querySelector('#progress').value");
Thread.Sleep(100);
}
catch (Exception)
{
break;
}
}
string fileTitle = (string)downloadWindowExecutor.ExecuteScript("return document.querySelector('downloads-manager').shadowRoot.querySelector('#downloadsList downloads-item').shadowRoot.querySelector('#show').getAttribute('title')");
downloadWindowExecutor.ExecuteScript("window.close()");
return fileTitle;
}
Then You can use file Path to rename it to whatever you need
Related
I have put together the following code below to convert some docx files to pdf. Since it is a heavy operation I figured I would do this asynchronously since they document isnt used right away for anything else and is for visual purposes only on my front end which I account for loading in javascript.
My code is as follows:
public static void ConvertToPDF(string PathToItemToConvert, string PathToLibrePortable)
{
try
{
string fileName = Path.GetFileName(PathToItemToConvert);
string fileDir = Path.GetDirectoryName(PathToItemToConvert);
var pdfProcess = new Process();
pdfProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
pdfProcess.StartInfo.FileName = PathToLibrePortable;
pdfProcess.StartInfo.Arguments =
String.Format("--norestore --nofirststartwizard --headless --convert-to pdf \"{0}\""
, fileName);
pdfProcess.StartInfo.WorkingDirectory = fileDir;
pdfProcess.StartInfo.RedirectStandardOutput = true;
pdfProcess.StartInfo.RedirectStandardError = true;
pdfProcess.StartInfo.UseShellExecute = false;
pdfProcess.Start();
string output = pdfProcess.StandardOutput.ReadToEnd();
}
catch (Exception ex)
{
//log error
}
}
I use it in the following fashion in an API as follows:
public async Task<ActionResult> ConvertDocs()
{
await Task.Run(() =>
{
MyTools.ConvertToPDF(SomeFilePath, locationOfLibreOfficeSoffice);
});
await Task.Run(() =>
{
MyTools.ConvertToPDF(AnotherFilePath, locationOfLibreOfficeSoffice);
});
}
This does not draw any errors but the second pdf is never created. If I comment out the other pdf code the second one will create. Its almost as if libreoffice cannot convert two pdfs at once despite I am starting separate threads with Task.Run(). Is there a way to do this or am I messing up something?
I want the embedded GeckoFx 60 to download a file and then open it with the default app.
By default it seems like GeckoFx does not do anything when the client requests to download a file.
To handle the download request I enabled an event handler:
LauncherDialog.Download += LauncherDialog_Download;
Then I found two possibilities to download or open a file via the HelperAppLauncher.
This one saves the requested file to a temp folder and opens it:
private void LauncherDialog_Download(object sender, LauncherDialogEvent e)
{
// direct open, file will be stored in C:\Users\Username\AppData\Local\Temp\
e.HelperAppLauncher.LaunchWithApplication(null, false);
}
I did not find a way to configure the save path. This other possible solution allows me to set the save path myself:
private void LauncherDialog_Download(object sender, LauncherDialogEvent e)
{
nsILocalFileWin objTarget = Xpcom.CreateInstance<nsILocalFileWin>("#mozilla.org/file/local;1");
var downloadPath = #Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\CustomFolder\\" + e.Filename;
using (nsAString tmp = new nsAString(downloadPath))
{
objTarget.InitWithPath(tmp);
}
e.HelperAppLauncher.SaveToDisk(objTarget, false);
Task.Run(() =>
{
Thread.Sleep(3000);
System.Diagnostics.Process.Start(downloadPath);
});
}
That Task.Run() works, but is quite ugly and error prone. I could not find a better solution though. I tried adding a WebProgressListener like this:
var webProgressListener = new WebProgressListener();
webProgressListener.OnStatusChangeCallback+= OnStatusChangeCallback;
e.HelperAppLauncher.SetWebProgressListener(webProgressListener);
webProgressListener.IsListening is true, but my method OnStatusChangeCallback is never called. Am I doing something wrong? Is there a newer way?
How can I get notified that the download is completed?
Or how do I set the path for LaunchWithApplication?
Not the best solution but here is my solution :
Task.Run(() =>
{
long sizefirst = 0;
while (true)
{
Thread.Sleep(1000);
if (File.Exists(downloadPath))
{
if (sizefirst == 0)
{
sizefirst = new FileInfo(downloadPath).Length;
continue;
}
long len_now = new FileInfo(downloadPath).Length;
if (len_now > sizefirst)
{
sizefirst = len_now;
continue;
}
else
{
System.Diagnostics.Process.Start(downloadPath);
break;
}
}
}
});
I have a project where I need to automatically download and process an excel file from a public web site.
the site is the following:
http://apps.ahca.myflorida.com/dm_web/(S(rhlzd0ac2qwvvccbyp3lx2or))/doc_results_fo.aspx
you can see a link called Export Results which downloads the excel file. This link does a postback and I have been looking all over the place to find a way to automate it without success.
This is the last code I tried:
try
{
byte[] b = webClient.DownloadData("http://apps.ahca.myflorida.com/dm_web/(S(eha2oijpqo5mro1aywok4lly))/doc_results_fo.aspx");
string s = System.Text.Encoding.UTF8.GetString(b);
var __EVENTVALIDATION = ExtractVariable(s, "__EVENTVALIDATION");
var forms = new NameValueCollection();
forms["__EVENTTARGET"] = "lbtSpreadsheet";
forms["__EVENTARGUMENT"] = "";
forms["__VIEWSTATE"] = ExtractVariable(s, "__VIEWSTATE");
forms["mTbdate"] = "11%2F15%2F2011";
forms["__EVENTVALIDATION"] = __EVENTVALIDATION;
webClient.Headers.Set(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded");
var responseData = webClient.UploadValues(#"http://apps.ahca.myflorida.com/dm_web/(S(eha2oijpqo5mro1aywok4lly))/doc_results_fo.aspx", "POST", forms);
System.IO.File.WriteAllBytes(#"c:\tmp\FLORIDA.xls", responseData);
}
catch (Exception ex)
{
Console.Write(ex.StackTrace);
}
}
private static string ExtractVariable(string s, string valueName)
{
string tokenStart = valueName + "\" value=\"";
string tokenEnd = "\" />";
int start = s.IndexOf(tokenStart) + tokenStart.Length;
int length = s.IndexOf(tokenEnd, start) - start;
return s.Substring(start, length);
}
This is supposed to get the value of view state and other fields and issue a POST , but when I run it the file that gets downloaded is the page itself and not the excel file.
I am not sure if this is possible using WebClient, or I should use WebBrowser control (or similar controls), or maybe an atomated browsing tool that I can record a sequence of steps and run it every x days.
Any help will be greatly appreciated.
Thank you
I figured it out using Selenium .NET
FirefoxProfile firefoxProfile = new FirefoxProfile();
firefoxProfile.SetPreference("browser.download.folderList", 2);
firefoxProfile.SetPreference("browser.download.dir", strFullpath);
firefoxProfile.SetPreference("browser.helperApps.neverAsk.openFile", "application/vnd.ms-Excel");
firefoxProfile.SetPreference("browser.helperApps.neverAsk.saveToDisk", "application/vnd.ms-Excel");
IWebDriver driver = new FirefoxDriver(firefoxProfile);
driver.Navigate().GoToUrl(link);
driver.FindElement(By.Id(lookup)).Click();
driver.Quit();
SetUpTest:
public void SetupTest()
{
IWebDriver driver = new ChromeDriver();
selenium = new DefaultSelenium(
"localhost",
4444,
"*googlechrome C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
"http://localhost");
selenium.Start();
verificationErrors = new StringBuilder();
}
Test function:
[Test]
public void LoginTest()
{
selenium.Open("http://localhost:8085/");
// login
for (int second = 0; ; second++)
{
if (second >= 60) Assert.Fail("timeout");
try
{
if (IsElementPresent(By.CssSelector("#username"))) break;
}
catch (Exception)
{ }
Thread.Sleep(1000);
}
driver.FindElement(By.Id("username")).SendKeys("admin");
driver.FindElement(By.Id("password")).SendKeys("123456");
driver.FindElement(By.CssSelector(".btn.btn-primary")).Click();
}
private bool IsElementPresent(By by)
{
try
{
driver.FindElement(by);
return true;
}
catch (NoSuchElementException)
{
return false;
}
}
I download Chrome Driver at this link: http://chromedriver.storage.googleapis.com/index.html
The newest version is 2.7.
My chrome version is 31.0.1650.63.
The problem is, driver cannot find element, although it exists in view.
How to make it work?
I'd recommend Selenium IDE and login manually while recording in Selenium IDE. It will help you identifying the correct names of the elements you want to select.
I use something like that
using Selenium;
ISelenium sel = new DefaultSelenium("localhost", 4444, "*firefox", "");
sel.Start();
sel.Open("www.whateveryourwebsideis.com");
sel.Type("id=user_email", "username");
sel.Type("id=user_password", "password");
sel.Click("name=commit");
Update:
seems to me as if you don't use your IDriver to navigate.
You have
selenium.Open("http://localhost:8085/");
but I guess you should use
driver.Navigate().GoToUrl("http://localhost:8085/");
Try
string htmlSource = driver.PageSource;
after loading the page to check if you actually have any HTML to search elements in.
I just tried installing ChromeDriver but it doesn't really work and I don't actually need it, so I'm afraid I have to leave it to you to find a solution...good luck.
I am trying to do following with c#.
1) Open Firefox Browser-->Tools-->Options-->General Tab--->Downloads--->Always ask me where to save file.
I want to do this whole process in my application with c#. I want that when download window opens, the radio button in "Always ask me where to save file" option gets checked automatically.
I have tried from various links, but all is in vain.
Here is the full code, console application.
Summary: preferences file is located in application roaming folder, something like this on windows 7:
C:\Users\MYNAME\AppData\Roaming\Mozilla\Firefox\Profiles\d9i9jniz.default\prefs.js
We alter this file so that it includes "user_pref("browser.download.useDownloadDir", false);"
Restart firefox, and done. Only run this application when firefox is not running.
static void Main(string[] args)
{
if (isFireFoxOpen())
{
Console.WriteLine("Firefox is open, close it");
}
else
{
string pathOfPrefsFile = GetPathOfPrefsFile();
updateSettingsFile(pathOfPrefsFile);
Console.WriteLine("Done");
}
Console.ReadLine();
}
private static void updateSettingsFile(string pathOfPrefsFile)
{
string[] contentsOfFile = File.ReadAllLines(pathOfPrefsFile);
// We are looking for "user_pref("browser.download.useDownloadDir", true);"
// This needs to be set to:
// "user_pref("browser.download.useDownloadDir", false);"
List<String> outputLines = new List<string>();
foreach (string line in contentsOfFile)
{
if (line.StartsWith("user_pref(\"browser.download.useDownloadDir\""))
{
Console.WriteLine("Found it already in file, replacing");
}
else
{
outputLines.Add(line);
}
}
// Finally add the value we want to the end
outputLines.Add("user_pref(\"browser.download.useDownloadDir\", false);");
// Rename the old file preferences for safety...
File.Move(pathOfPrefsFile, Path.GetDirectoryName(pathOfPrefsFile) + #"\" + Path.GetFileName(pathOfPrefsFile) + ".OLD." + Guid.NewGuid().ToString());
// Write the new file.
File.WriteAllLines(pathOfPrefsFile, outputLines.ToArray());
}
private static string GetPathOfPrefsFile()
{
// Get roaming folder, and get the profiles.ini
string iniFilePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + #"\Mozilla\Firefox\profiles.ini";
// Profiles.ini tells us what folder the preferences file is in.
string contentsOfIni = File.ReadAllText(iniFilePath);
int locOfPath = contentsOfIni.IndexOf("Path=Profiles");
int endOfPath = contentsOfIni.IndexOf(".default", locOfPath);
int startOfPath = locOfPath + "Path=Profiles".Length + 1;
int countofCopy = ((endOfPath + ".default".Length) - startOfPath);
string path = contentsOfIni.Substring(startOfPath, countofCopy);
string toReturn = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + #"\Mozilla\Firefox\Profiles\" + path + #"\prefs.js";
return toReturn;
}
public static bool isFireFoxOpen()
{
foreach (Process proc in Process.GetProcesses())
{
if (proc.ProcessName == "firefox")
{
return true;
}
}
return false;
}
What have you tried?
Firefox settings are stored in your profile, so I'd guess you can change the contents of the given file. Type about:config to find the setting you're looking for, I guess it's in the browser.download tree, alter it (after you made sure the browser isn't running) and you should be good to go.