Selenium Grid Download File

Selenium V3/V2 grid download file example code is shown below. See Selenium 4 example. Validate downloaded files to the grid node by using "chrome://downloads" to either just check that they did download or do further validation locally by downloading the file to your local machine.

The main advantage of using "chrome://downloads" is that it only displays the files downloaded in the session and ignores previously downloaded files to the grid node. Make sure you check for downloaded files in the same selenium session, maybe in another tab or window, as the next test is routed to next available grid node and will not necessary run on the grid node you downloaded the files to.

NOTE: if you do download a file to local, typically keep the file size below 50 mb as base64 encoding do enlarge the file on the node when read and are subject to resource limitations. Do let us know if you have a test scenario requiring downloading files larger than 50mb.

Also see our selenium grid files upload example.

Java Example

This java example code is also available as a maven project on Github.

package download;

import java.io.File;
import java.io.FileOutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

import org.apache.commons.codec.binary.Base64;
import org.openqa.selenium.Platform;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.RemoteWebDriver;

import junit.framework.TestCase;

public class selenium_grid_download_files extends TestCase {
	 private RemoteWebDriver driver;

	 //NOTE: find these credentials in your Gridlastic dashboard after launching your selenium grid (get a free account).
	 String video_url = System.getenv("VIDEO_URL");
	 String hub = System.getenv("HUB"); // like "https://USERNAME:ACCESS_KEY@HUB_SUBDOMAIN.gridlastic.com/wd/hub";

	 
	 public void setUp() throws Exception {
	     
	 	ChromeOptions options = new ChromeOptions();
	 	options.setCapability("version", "latest");
	 	options.setCapability("platform", Platform.WIN10);
	 	options.setCapability("platformName", "windows");
	 	options.setCapability("video", "True");
	 		 	
	 	
        HashMap chromePrefs = new HashMap();
        chromePrefs.put("download.prompt_for_download", Boolean.valueOf(false));
        chromePrefs.put("plugins.always_open_pdf_externally", Boolean.valueOf(true));
        chromePrefs.put("safebrowsing_for_trusted_sources_enabled", Boolean.valueOf(false));
        options.setExperimentalOption("prefs", chromePrefs);
        
    
	     driver = new RemoteWebDriver(
	        new URL(hub),
	        options);
	     driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
	     System.out.println("GRIDLASTIC VIDEO URL: " + video_url + ((RemoteWebDriver) driver).getSessionId()); 
	 }

	 public void test_download_file() throws Exception {
		
			File download_url = new File((String) "https://static.mozilla.com/foundation/documents/mf-articles-of-incorporation.pdf");		
			driver.get(download_url.toString());
			
			  int count = 1;
		        do {
		        	if (get_downloaded_files((RemoteWebDriver) driver).toString().contains(  (download_url.getName().substring(0,download_url.getName().indexOf(".")-1)) )){ //Note: multiple file downloads on the same grid node of the same file name will increment the file name like 50MB(2).zip 
		        		System.out.println("FILE DOWNLOADED TO GRID NODE");
		    			break;
		    		} else {
		    			System.out.println("DOWNLOAD PROGRESS: " + get_download_progress_all((RemoteWebDriver) driver));	
		    		}
		             count++;
		             Thread.sleep(10000);
		        } while (count < 11);
		        
		        
		        ArrayList downloaded_files_arraylist = get_downloaded_files((RemoteWebDriver) driver);
		    	String content = get_file_content((RemoteWebDriver) driver,(String) downloaded_files_arraylist.get(0));// large files might need and increase in implicit wait.
		    	 try {		    	    	
		    	    	String home = System.getProperty("user.home");
		    	    	FileOutputStream fos = new FileOutputStream(home+"/downloads/gridnodes/" + download_url .getName());
		    	        byte[] decoder = Base64.decodeBase64(content.substring(content.indexOf("base64,")+7));
		    	        fos.write(decoder);
		    	        System.out.println("File saved to local.");
		    	      } catch (Exception e) {
		    	        e.printStackTrace();
		    	      }
	}

	 public void tearDown() throws Exception {
	     driver.quit();
	 }
	 
	 
	 private static String get_file_content(RemoteWebDriver remoteDriver,String path) {
			String file_content = null;
				try {
					if(!remoteDriver.getCurrentUrl().startsWith("chrome://downloads")) {
					remoteDriver.get("chrome://downloads/");
					}


				    WebElement elem = (WebElement) remoteDriver.executeScript(
						    "var input = window.document.createElement('INPUT'); "+
						    "input.setAttribute('type', 'file'); "+
						    "input.hidden = true; "+
						    "input.onchange = function (e) { e.stopPropagation() }; "+
						    "return window.document.documentElement.appendChild(input); "
						    ,"" );
					
					 elem.sendKeys(path);
				
				 file_content = (String) remoteDriver.executeAsyncScript(
							    "var input = arguments[0], callback = arguments[1]; "+
							    "var reader = new FileReader(); "+
							    "reader.onload = function (ev) { callback(reader.result) }; "+
							    "reader.onerror = function (ex) { callback(ex.message) }; "+
							    "reader.readAsDataURL(input.files[0]); "+
							    "input.remove(); "
							    , elem);
					
					if (!file_content.startsWith("data:")){
						System.out.println("Failed to get file content");
					}
				
				} catch (Exception e) {
					System.err.println(e);
				}
return file_content;

			}
	
		 
		 
		 private static ArrayList get_downloaded_files(RemoteWebDriver remoteDriver) {
			 ArrayList filesFound = null;
				try {
					if(!remoteDriver.getCurrentUrl().startsWith("chrome://downloads")) {
					remoteDriver.get("chrome://downloads/");
					}
					filesFound =  (ArrayList)  remoteDriver.executeScript(
					  "return  document.querySelector('downloads-manager')  "+
				      " .shadowRoot.querySelector('#downloadsList')         "+
				      " .items.filter(e => e.state === 'COMPLETE')          "+
				      " .map(e => e.filePath || e.file_path || e.fileUrl || e.file_url); ","");
				} catch (Exception e) {
					System.err.println(e);
				}
				return filesFound;
			}
		 
		 private static String get_download_progress(RemoteWebDriver remoteDriver) {
			 String progress = null;
				try {
					if(!remoteDriver.getCurrentUrl().startsWith("chrome://downloads")) {
					remoteDriver.get("chrome://downloads/");
					}
					progress=  (String) remoteDriver.executeScript(						
							"var tag = document.querySelector('downloads-manager').shadowRoot;"+
						    "var intag = tag.querySelector('downloads-item').shadowRoot;"+
						    "var progress_tag = intag.getElementById('progress');"+
						    "var progress = null;"+
						   " if(progress_tag) { "+
						    "    progress = progress_tag.value; "+
						  "  }" +
						    "return progress;"
							,"");
					
		
				} catch (Exception e) {
					System.err.println(e);
				}
				return progress;
			}

		 
		 
		 
		 private static ArrayList get_download_progress_all(RemoteWebDriver remoteDriver) {
			 ArrayList progress = null;
				try {
					if(!remoteDriver.getCurrentUrl().startsWith("chrome://downloads")) {
					remoteDriver.get("chrome://downloads/");
					}
					progress=  (ArrayList) remoteDriver.executeScript(						
							" var tag = document.querySelector('downloads-manager').shadowRoot;" + 
							"			    var item_tags = tag.querySelectorAll('downloads-item');" + 
							"			    var item_tags_length = item_tags.length;" + 
							"			    var progress_lst = [];" + 
							"			    for(var i=0; i<item_tags_length; i++) {" + 
							"			        var intag = item_tags[i].shadowRoot;" + 
							"			        var progress_tag = intag.getElementById('progress');" + 
							"			        var progress = null;" + 
							"			        if(progress_tag) {" + 
							"			            var progress = progress_tag.value;" + 
							"			        }" + 
							"			        progress_lst.push(progress);" + 
							"			    }" + 
							"			    return progress_lst",
							"");
					
		
				} catch (Exception e) {
					System.err.println(e);
				}
				return progress;
			}	
		
	 
	}


Python Example

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
import os, time, base64
import unittest
import logging

class GridDownload(unittest.TestCase):

  logging.basicConfig(filename="log.txt", level=logging.INFO)

  fileUrl = "https://static.mozilla.com/foundation/documents/mf-articles-of-incorporation.pdf"

  hubUrl = "" # like "https://USERNAME:ACCESS_KEY@HUB_SUBDOMAIN.gridlastic.com/wd/hub"



  def setUp(self):
    self.capabilities_chrome = { \
      'browserName': 'chrome',
      'goog:chromeOptions': { \
        'args': [
        ],
        'prefs': { \
          'download.prompt_for_download': False,
          'plugins.always_open_pdf_externally': True,
          'safebrowsing_for_trusted_sources_enabled': False
        }
      }
    }


  def get_downloaded_files(self,driver):
    self.driver = driver
    if not self.driver.current_url.startswith("chrome://downloads"):
      driver.get("chrome://downloads/")

    return  self.driver.execute_script( \
      "return  document.querySelector('downloads-manager')  "
      " .shadowRoot.querySelector('#downloadsList')         "
      " .items.filter(e => e.state === 'COMPLETE')          "
      " .map(e => e.filePath || e.file_path || e.fileUrl || e.file_url); ")


  def get_file_content(self, driver,path):
    try:
      elem = driver.execute_script( \
        "var input = window.document.createElement('INPUT'); "
        "input.setAttribute('type', 'file'); "
        "input.hidden = true; "
        "input.onchange = function (e) { e.stopPropagation() }; "
        "return window.document.documentElement.appendChild(input); " )
      elem._execute('sendKeysToElement', {'value': [ path ], 'text': path})
      result = driver.execute_async_script( \
        "var input = arguments[0], callback = arguments[1]; "
        "var reader = new FileReader(); "
        "reader.onload = function (ev) { callback(reader.result) }; "
        "reader.onerror = function (ex) { callback(ex.message) }; "
        "reader.readAsDataURL(input.files[0]); "
        "input.remove(); "
        , elem)
      if not result.startswith('data:') :
        raise Exception("Failed to get file content: %s" % result)
      return base64.b64decode(result[result.find('base64,') + 7:])
    finally:
      logging.info("get_file_content executed successfully")


  def tear_down(self,driver):
    driver.quit()




  def test_grid_Download_Files(self):
    try:
      driver = webdriver.Remote(self.hubUrl, self.capabilities_chrome)
      # download a pdf file
      driver.get(self.fileUrl)
      # list all the completed remote files (waits for at least one)
      files = WebDriverWait(driver, 30, 1).until(lambda driver: self.get_downloaded_files(driver))
      # get the content of the first file remotely
      content = self.get_file_content(driver,files[0])
      # save the content in a local file in the working directory
      print(os.path.basename(files[0]))
      with open(os.path.basename(files[0]), 'wb') as f:
        f.write(content)
      self.tear_down(driver)
    finally:
      logging.info("Test test_grid_download_files executed successfully")

if __name__ == "__main__":
    unittest.main()
NOTE: Gridlastic auto scaling requires all 3 test environment parameters platform, browser and browser version to be specified in each request in order to launch test nodes to fulfill test demand. Video recording is optional. See test environments for capabilities options.
It is important to ensure that "driver.quit()" is always called for proper test execution and creation of video recordings of failed tests.