How to download a file using the remote selenium webdriver?

前端 未结 5 970
清歌不尽
清歌不尽 2020-12-09 18:24

I am using a remote selenium webdriver to perform some tests. At some point, however, I need to download a file and check its contents.

I am using the

5条回答
  •  有刺的猬
    2020-12-09 19:06

    This is just the Java version of @Florent 's answer above. With a lot of guidance from him and some digging and tweaking I was finally able to get it to work for Java. I figured I could save other people some time by laying it out here.

    Firefox

    First we need to create a custom firefox driver because we need to use the SET_CONTEXT command which is not implemented in the Java client(as of selenium - 3.141.59)

    public class CustomFirefoxDriver extends RemoteWebDriver{
    
    
        public CustomFirefoxDriver(URL RemoteWebDriverUrl, FirefoxOptions options) throws Exception {
            super(RemoteWebDriverUrl, options);
            CommandInfo cmd = new CommandInfo("/session/:sessionId/moz/context", HttpMethod.POST);
            Method defineCommand = HttpCommandExecutor.class.getDeclaredMethod("defineCommand", String.class, CommandInfo.class);
            defineCommand.setAccessible(true);
            defineCommand.invoke(super.getCommandExecutor(), "SET_CONTEXT", cmd);
        }
    
    
        public Object setContext(String context) {
            return execute("SET_CONTEXT", ImmutableMap.of("context", context)).getValue();
        }
    }
    

    The code below retrieves the content of a downloaded .xls file and saves it as a file(temp.xls) in the same directory where the Java class is run. In Firefox this is fairly straightforward as we can use the browser API

    public String getDownloadedFileNameBySubStringFirefox(String Matcher) {
    
        String fileName = "";
    
        ((CustomFirefoxDriver) driver).setContext("chrome");
    
        String script = "var { Downloads } = Components.utils.import('resource://gre/modules/Downloads.jsm', {});"
                + "Downloads.getList(Downloads.ALL).then(list => list.getAll())"
                + ".then(entries => entries.filter(e => e.succeeded).map(e => e.target.path))"
                + ".then(arguments[0]);";
    
        String fileNameList = js.executeAsyncScript(script).toString();
        String name = fileNameList.substring(1, fileNameList.length() -1);
    
        if(name.contains(Matcher)) {
            fileName = name;
        }
    
        ((CustomFirefoxDriver) driver).setContext("content");
    
        return fileName;
    }
    
    public void getDownloadedFileContentFirefox(String fileIdentifier) {
    
        String filePath = getDownloadedFileNameBySubStringFirefox(fileIdentifier);
        ((CustomFirefoxDriver) driver).setContext("chrome");
    
        String script = "var { OS } = Cu.import(\"resource://gre/modules/osfile.jsm\", {});" + 
                        "OS.File.read(arguments[0]).then(function(data) {" + 
                        "var base64 = Cc[\"@mozilla.org/scriptablebase64encoder;1\"].getService(Ci.nsIScriptableBase64Encoder);" +
                        "var stream = Cc['@mozilla.org/io/arraybuffer-input-stream;1'].createInstance(Ci.nsIArrayBufferInputStream);" +
                        "stream.setData(data.buffer, 0, data.length);" +
                        "return base64.encodeToString(stream, data.length);" +
                        "}).then(arguments[1]);" ;
    
        Object base64FileContent = js.executeAsyncScript(script, filePath);//.toString();
        try {
            Files.write(Paths.get("temp.xls"), DatatypeConverter.parseBase64Binary(base64FileContent.toString()));
        } catch (IOException i) {
            System.out.println(i.getMessage());
        }
    
    }
    

    Chrome

    We need to employ a different approach to achieve the same goal in Chrome. We append an input file element to the Downloads page and pass the file location to this element. Once this element points to our required file, we use it to read its content.

    public String getDownloadedFileNameBySubStringChrome(String Matcher) {
        String file = "";
        //The script below returns the list of files as a list of the form '[$FileName1, $FileName2...]'
        // with the most recently downloaded file listed first.
        String script = "return downloads.Manager.get().items_.filter(e => e.state === 'COMPLETE').map(e => e.file_url);" ;
        if(!driver.getCurrentUrl().startsWith("chrome://downloads/")) {
            driver.get("chrome://downloads/");
            }
        String fileNameList =  js.executeScript(script).toString();
        //Removing square brackets
        fileNameList = fileNameList.substring(1, fileNameList.length() -1);
        String [] fileNames = fileNameList.split(",");
        for(int i=0; i

    The reason I needed this setup was because my test suite was running on a Jenkin's server; and the Selenium Grid hub and Node set up it was pointing to was running in Docker containers(https://github.com/SeleniumHQ/docker-selenium) on a different server. Once again, this is just a Java translation of @Florent 's answer above. Please refer it for more info.

提交回复
热议问题