在 IE9 中使用 Watin 下载文件

发布于 2024-11-09 08:39:22 字数 1117 浏览 0 评论 0原文

我在自动执行从网站下载文件的过程时遇到问题。该网站有一个 Java 按钮,单击该按钮会触发 Excel 文件的下载。我正在使用最新版本的 Watin (v2.1)。

我已经成功让 Watin 登录该网站,导航到相应的页面,更改页面上的参数,然后单击按钮开始下载。

但是,当下载完成后,会出现 IE9 下载框,但没有任何反应,直到 Watin 超时。

我将不胜感激任何建议,因为我看不到任何下载文件或保存文件的方法。即使它将“Alt+S”传递到页面,也会保存它。我尝试通过 WatinTestRecorder 运行它,但不会提示保存。

using (var browser = new IE(sLogin))
{
    browser.AddDialogHandler(new OKDialogHandler());
    browser.AddDialogHandler(new DialogHandlerHelper());
    browser.AddDialogHandler(new ConfirmDialogHandler());
    browser.AddDialogHandler(new ReturnDialogHandlerIe9());

    browser.TextField(Find.ByName("txtUserID")).TypeText("username");
    browser.TextField(Find.ByName("txtPassword")).TypeText("password");
    browser.Button(Find.ByName("btnLogin")).Click();

    browser.WaitForComplete();  
    browser.GoTo(targetUri);

    browser.SelectList("ctl00_phFormContent_ucOptionParam0_lst").SelectByValue("4");

    browser.Button(Find.ByName("ctl00$phFormButtonBar$btnRun")).Click();
    browser.WaitForComplete();

    //Some code to download the file here!
}

I'm having an issue with automating the process of downloading a file from a website. The website has a Java button, that when clicked, triggers the download of an Excel file. I'm using the most recent build of Watin (v2.1).

I've managed to get Watin to log into the website, navigate to the appropriate page, change parameters on the page, and click the button to start the download.

However, when the download has completed, the IE9 download box appears, and nothing happens, until Watin timesout.

I'd appreciate any suggestions as I can't see any way of downloading a file, or getting it to save the file. Even if it passed 'Alt+S' to the page, that would save it. I've tried running it through WatinTestRecorder and that doesn't prompt for saving.

using (var browser = new IE(sLogin))
{
    browser.AddDialogHandler(new OKDialogHandler());
    browser.AddDialogHandler(new DialogHandlerHelper());
    browser.AddDialogHandler(new ConfirmDialogHandler());
    browser.AddDialogHandler(new ReturnDialogHandlerIe9());

    browser.TextField(Find.ByName("txtUserID")).TypeText("username");
    browser.TextField(Find.ByName("txtPassword")).TypeText("password");
    browser.Button(Find.ByName("btnLogin")).Click();

    browser.WaitForComplete();  
    browser.GoTo(targetUri);

    browser.SelectList("ctl00_phFormContent_ucOptionParam0_lst").SelectByValue("4");

    browser.Button(Find.ByName("ctl00$phFormButtonBar$btnRun")).Click();
    browser.WaitForComplete();

    //Some code to download the file here!
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

过去的过去 2024-11-16 08:39:22

自版本 1.1.0.4000 起应支持此功能。该版本的发行说明不再在线 (http://watin.org/documentation/),但我在谷歌缓存中找到了它(http://svn6.assembla.com/svn/ci-samples/dotnet/watir/website/releasenotes-1-1-0-4000.html

应该是这样的:

using(IE ie = new IE(someUrlToGoTo))
{
    FileDownloadHandler fileDownloadHandler = new FileDownloadHandler(fullFileName);
    ie.AddDialogHandler(fileDownloadHandler);

    ie.Link("startDownloadLinkId").Click();

    fileDownloadHandler.WaitUntilFileDownloadDialogIsHandled(15);
    fileDownloadHandler.WaitUntilDownloadCompleted(200);
}

编辑:
经过下面的评论,这个答案被接受。所以我假设以下代码有效(取自我上一条评论中的 SourceForge 链接,请注意 ClickNoWait):

using(IE ie = new IE(someUrlToGoTo))
{
    FileDownloadHandler fileDownloadHandler = new FileDownloadHandler(fullFileName);
    ie.AddDialogHandler(fileDownloadHandler);

    ie.Link("startDownloadLinkId").ClickNoWait();

    fileDownloadHandler.WaitUntilFileDownloadDialogIsHandled(15);
    fileDownloadHandler.WaitUntilDownloadCompleted(200);
}

This should be supported since version 1.1.0.4000. The release notes for that version aren't online anymore (http://watin.org/documentation/), but I found it in Googles cache (http://svn6.assembla.com/svn/ci-samples/dotnet/watir/website/releasenotes-1-1-0-4000.html)

It should be something like:

using(IE ie = new IE(someUrlToGoTo))
{
    FileDownloadHandler fileDownloadHandler = new FileDownloadHandler(fullFileName);
    ie.AddDialogHandler(fileDownloadHandler);

    ie.Link("startDownloadLinkId").Click();

    fileDownloadHandler.WaitUntilFileDownloadDialogIsHandled(15);
    fileDownloadHandler.WaitUntilDownloadCompleted(200);
}

EDIT:
After the comments below, this answer was accepted. So I'm assuming the following code works (which is taken from the link to SourceForge in my last comment, notice the ClickNoWait):

using(IE ie = new IE(someUrlToGoTo))
{
    FileDownloadHandler fileDownloadHandler = new FileDownloadHandler(fullFileName);
    ie.AddDialogHandler(fileDownloadHandler);

    ie.Link("startDownloadLinkId").ClickNoWait();

    fileDownloadHandler.WaitUntilFileDownloadDialogIsHandled(15);
    fileDownloadHandler.WaitUntilDownloadCompleted(200);
}
夜空下最亮的亮点 2024-11-16 08:39:22

接受的答案对我不起作用,因为 IE 9 会弹出一个“通知”,您必须导航才能进入实际的“另存为”对话框(并且 WatiN 无法自动处理通知)。我点击了鲍里斯·巴甫洛夫(Borris Pavlov)的链接,该链接很好地指明了方向。我稍微清理了发布在那里的代码,这就是生成的文件:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WatiN.Core;
using WatiN.Core.Native.Windows;
using System.Threading;
using System.Windows.Automation;

namespace MyProject
{
    public static class BrowserExtensionMethods
    {
        public static void DownloadIeFile(this IE browser,string saveAsFilename=null)
        {
            // see information here (http://msdn.microsoft.com/en-us/library/windows/desktop/ms633515(v=vs.85).aspx)
            Window windowMain = new Window(NativeMethods.GetWindow(browser.hWnd, 5));
            TreeWalker dialogElementTreeWalker = new TreeWalker(Condition.TrueCondition);
            AutomationElement mainWindow = dialogElementTreeWalker.GetParent(AutomationElement.FromHandle(browser.hWnd));

            Window windowDialog = new Window(NativeMethods.GetWindow(windowMain.Hwnd, 5));
            // if doesn't work try to increase sleep interval or write your own waitUntill method
            Thread.Sleep(1000);
            windowDialog.SetActivate();
            AutomationElementCollection dialogElements = AutomationElement.FromHandle(windowDialog.Hwnd).FindAll(TreeScope.Children, Condition.TrueCondition);

            if (string.IsNullOrEmpty(saveAsFilename))
            {
                ClickSave(dialogElements);
            }
            else
            {
                ClickSaveAs(mainWindow, dialogElements,saveAsFilename);
            }
        }

        private static void ClickSaveAs(AutomationElement mainWindow, AutomationElementCollection dialogElements,string filename)
        {
            foreach (AutomationElement element in dialogElements)
            {

                if (element.Current.Name.Equals("Save"))
                {
                    AutomationElementCollection dialogSubElements = element.FindAll(TreeScope.Children, Automation.ControlViewCondition);
                    InvokePattern clickPatternForSaveDropdown = (InvokePattern)dialogSubElements[0].GetCurrentPattern(AutomationPattern.LookupById(10000));
                    clickPatternForSaveDropdown.Invoke();
                    Thread.Sleep(3000);

                    AutomationElementCollection dialogElementsInMainWindow = mainWindow.FindAll(TreeScope.Children, Condition.TrueCondition);
                    foreach (AutomationElement currentMainWindowDialogElement in dialogElementsInMainWindow)
                    {
                        if (currentMainWindowDialogElement.Current.LocalizedControlType == "menu")
                        {
                            // first array element 'Save', second array element 'Save as', third second array element    'Save and open'
                            InvokePattern clickMenu = (InvokePattern)currentMainWindowDialogElement.FindAll(TreeScope.Children, Condition.TrueCondition)[1].GetCurrentPattern(AutomationPattern.LookupById(10000));
                            clickMenu.Invoke();
                            Thread.Sleep(5000);
                            ControlSaveDialog(mainWindow, filename);
                            break;

                        }
                    }
                }
            }
        }

        private static void ClickSave(AutomationElementCollection dialogElements)
        {
            foreach (AutomationElement element in dialogElements)
            {
                // You can use "Save ", "Open", ''Cancel', or "Close" to find necessary button Or write your own enum
                if (element.Current.Name.Equals("Save"))
                {
                    // if doesn't work try to increase sleep interval or write your own waitUntil method
                    // WaitUntilButtonExsist(element,100);
                    Thread.Sleep(1000);
                    AutomationPattern[] automationPatterns = element.GetSupportedPatterns();
                    // replace this foreach if you need 'Save as' with code bellow
                    foreach (AutomationPattern currentPattern in automationPatterns)
                    {
                        // '10000' button click event id 
                        if (currentPattern.Id == 10000)
                        {
                            InvokePattern click = (InvokePattern)element.GetCurrentPattern(currentPattern);
                            click.Invoke();
                        }
                    }
                }
            }
        }

        private static void ControlSaveDialog(AutomationElement mainWindow, string path)
        {
            //obtain the save as dialog
            //*** must disable throwing of the NonComVisibleBaseClass "exception" for this to work in debug mode:
            //              1. Navigate to Debug->Exceptions...
            //              2. Expand "Managed Debugging Assistants"
            //              3. Uncheck the NonComVisibleBaseClass Thrown option.
            //              4. Click [Ok]
            //***copied from http://social.msdn.microsoft.com/Forums/en-US/27c3bae8-41fe-4db4-8022-e27d333f714e/noncomvisiblebaseclass-was-detected?forum=Vsexpressvb

            var saveAsDialog = mainWindow.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "Save As"));
            //var saveAsDialog = mainWindow.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "[#] Save As [#]"));  //needed if using sandboxie
            //get the file name box
            var saveAsText = saveAsDialog
                    .FindFirst(TreeScope.Descendants,
                               new AndCondition(
                                   new PropertyCondition(AutomationElement.NameProperty, "File name:"),
                                   new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit)))
                    .GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
            //fill the filename box 
            saveAsText.SetValue(path);

            Thread.Sleep(1000);
            //find the save button
            var saveButton =
                    saveAsDialog.FindFirst(TreeScope.Descendants,
                    new AndCondition(
                        new PropertyCondition(AutomationElement.NameProperty, "Save"),
                        new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button)));
            //invoke the button
            var pattern = saveButton.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
            pattern.Invoke();
        }
    }
}

请参阅 ControlSaveDialog 方法中的注释,了解有关如何使其作为调试构建(VS 设置)工作的信息。

我实际上只测试了“另存为”操作,所以我希望其他的作品(似乎原始海报添加了“另存为”作为事后的想法,所以我的猜测是他更彻底地测试了“保存”操作)。

要调用它,您可以使用一些代码,例如:

Link lastMp4Link = mp4Links[mp4Links.Count - 1];  //mp4Links is a WatiN.Core.LinkCollection
lastMp4Link.Click();

browser.DownloadIeFile(string.Format(@"c:\temp\myFile.blah"));  //"browser" is a WatiN.Core.IE object

The accepted answer did not work for me because IE 9 pops up a "Notification" you have to navigate before you can get to the actual Save As dialog (and WatiN cannot handle notifications automatically). I followed Borris Pavlov's link which did a good job showing the way. I cleaned up the code a bit that was posted there and this was the resulting file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WatiN.Core;
using WatiN.Core.Native.Windows;
using System.Threading;
using System.Windows.Automation;

namespace MyProject
{
    public static class BrowserExtensionMethods
    {
        public static void DownloadIeFile(this IE browser,string saveAsFilename=null)
        {
            // see information here (http://msdn.microsoft.com/en-us/library/windows/desktop/ms633515(v=vs.85).aspx)
            Window windowMain = new Window(NativeMethods.GetWindow(browser.hWnd, 5));
            TreeWalker dialogElementTreeWalker = new TreeWalker(Condition.TrueCondition);
            AutomationElement mainWindow = dialogElementTreeWalker.GetParent(AutomationElement.FromHandle(browser.hWnd));

            Window windowDialog = new Window(NativeMethods.GetWindow(windowMain.Hwnd, 5));
            // if doesn't work try to increase sleep interval or write your own waitUntill method
            Thread.Sleep(1000);
            windowDialog.SetActivate();
            AutomationElementCollection dialogElements = AutomationElement.FromHandle(windowDialog.Hwnd).FindAll(TreeScope.Children, Condition.TrueCondition);

            if (string.IsNullOrEmpty(saveAsFilename))
            {
                ClickSave(dialogElements);
            }
            else
            {
                ClickSaveAs(mainWindow, dialogElements,saveAsFilename);
            }
        }

        private static void ClickSaveAs(AutomationElement mainWindow, AutomationElementCollection dialogElements,string filename)
        {
            foreach (AutomationElement element in dialogElements)
            {

                if (element.Current.Name.Equals("Save"))
                {
                    AutomationElementCollection dialogSubElements = element.FindAll(TreeScope.Children, Automation.ControlViewCondition);
                    InvokePattern clickPatternForSaveDropdown = (InvokePattern)dialogSubElements[0].GetCurrentPattern(AutomationPattern.LookupById(10000));
                    clickPatternForSaveDropdown.Invoke();
                    Thread.Sleep(3000);

                    AutomationElementCollection dialogElementsInMainWindow = mainWindow.FindAll(TreeScope.Children, Condition.TrueCondition);
                    foreach (AutomationElement currentMainWindowDialogElement in dialogElementsInMainWindow)
                    {
                        if (currentMainWindowDialogElement.Current.LocalizedControlType == "menu")
                        {
                            // first array element 'Save', second array element 'Save as', third second array element    'Save and open'
                            InvokePattern clickMenu = (InvokePattern)currentMainWindowDialogElement.FindAll(TreeScope.Children, Condition.TrueCondition)[1].GetCurrentPattern(AutomationPattern.LookupById(10000));
                            clickMenu.Invoke();
                            Thread.Sleep(5000);
                            ControlSaveDialog(mainWindow, filename);
                            break;

                        }
                    }
                }
            }
        }

        private static void ClickSave(AutomationElementCollection dialogElements)
        {
            foreach (AutomationElement element in dialogElements)
            {
                // You can use "Save ", "Open", ''Cancel', or "Close" to find necessary button Or write your own enum
                if (element.Current.Name.Equals("Save"))
                {
                    // if doesn't work try to increase sleep interval or write your own waitUntil method
                    // WaitUntilButtonExsist(element,100);
                    Thread.Sleep(1000);
                    AutomationPattern[] automationPatterns = element.GetSupportedPatterns();
                    // replace this foreach if you need 'Save as' with code bellow
                    foreach (AutomationPattern currentPattern in automationPatterns)
                    {
                        // '10000' button click event id 
                        if (currentPattern.Id == 10000)
                        {
                            InvokePattern click = (InvokePattern)element.GetCurrentPattern(currentPattern);
                            click.Invoke();
                        }
                    }
                }
            }
        }

        private static void ControlSaveDialog(AutomationElement mainWindow, string path)
        {
            //obtain the save as dialog
            //*** must disable throwing of the NonComVisibleBaseClass "exception" for this to work in debug mode:
            //              1. Navigate to Debug->Exceptions...
            //              2. Expand "Managed Debugging Assistants"
            //              3. Uncheck the NonComVisibleBaseClass Thrown option.
            //              4. Click [Ok]
            //***copied from http://social.msdn.microsoft.com/Forums/en-US/27c3bae8-41fe-4db4-8022-e27d333f714e/noncomvisiblebaseclass-was-detected?forum=Vsexpressvb

            var saveAsDialog = mainWindow.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "Save As"));
            //var saveAsDialog = mainWindow.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "[#] Save As [#]"));  //needed if using sandboxie
            //get the file name box
            var saveAsText = saveAsDialog
                    .FindFirst(TreeScope.Descendants,
                               new AndCondition(
                                   new PropertyCondition(AutomationElement.NameProperty, "File name:"),
                                   new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit)))
                    .GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
            //fill the filename box 
            saveAsText.SetValue(path);

            Thread.Sleep(1000);
            //find the save button
            var saveButton =
                    saveAsDialog.FindFirst(TreeScope.Descendants,
                    new AndCondition(
                        new PropertyCondition(AutomationElement.NameProperty, "Save"),
                        new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button)));
            //invoke the button
            var pattern = saveButton.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
            pattern.Invoke();
        }
    }
}

See the comment in the ControlSaveDialog method for info on how to make this work as a debug build (VS settings).

I actually only tested the "Save As" operation so I hope the other works (it seems that the original poster added the "Save As" as an afterthought so my guess is that he tested the "Save" operation more thoroughly).

To call this you can use some code like:

Link lastMp4Link = mp4Links[mp4Links.Count - 1];  //mp4Links is a WatiN.Core.LinkCollection
lastMp4Link.Click();

browser.DownloadIeFile(string.Format(@"c:\temp\myFile.blah"));  //"browser" is a WatiN.Core.IE object
忆梦 2024-11-16 08:39:22

我刚刚从 Watin 用户邮件列表中得知 FileDownloadHandler 在 IE9 中被破坏了。目前还没有修复,但是我卸载了 IE9(回滚到 IE8),它有由 WatiN 处理的旧样式对话框。

I just got word from the Watin-users mailing list that FileDownloadHandler is broken with IE9. There is no fix yet, however I uninstalled IE9 (roll back to IE8) and it has the old style dialog that is handled by WatiN.

时光与爱终年不遇 2024-11-16 08:39:22

Watin 找不到下载对话框和对话框按钮。可以用这个来解决。

如何使用 Watin / IE9 测试文件下载?

查看评论

Watin can't find download dialog and dialog buttons. It can resolve with this.

How to test file download with Watin / IE9?

See comments

且行且努力 2024-11-16 08:39:22

使用 WaitN 进行多次修改后,我最终使用了 Selenium。它使您可以更好地控制正在处理和使用的内容,并允许您使用更现代的网络浏览器。

After many revisions using WaitN, I ended up using Selenium. It gives you a ton more control over what is being processed and used and allows you to use more modern web browsers.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文