Selenium: Download file in headless chrome

Created on 3 Dec 2017  ·  47Comments  ·  Source: SeleniumHQ/selenium

Meta -

OS: OSX
Selenium Version: 3.8.1
Browser: Chrome

Expected Behavior -

Permit files to be downloaded in headless mode.

Actual Behavior -

Files aren't downloaded.

Steps to reproduce -

Launch chrome in headless mode and try to download any file.

Looks like it is a feature to prevent sites from downloading files when running chrome in headless mode.
https://bugs.chromium.org/p/chromium/issues/detail?id=696481

To permit downloads it's necessary to send a command to chrome.
I'm using java and I couldn't find a way to make it work.

I even tried sending a post request to /session/:sessionId/chromium/send_command but that didn't work.

C-dotnet C-java D-chrome I-enhancement R-blocked on external

Most helpful comment

Worked for me with Ruby and Capybara:

page.driver.browser.download_path = YOUR_DOWNLOAD_PATH
click_link "Download stuff"

All 47 comments

Apparently, this new command needs to be added to all the bindings.

I managed to get it working by issuing the above request directly to the chromedriver but using a development build of chromium.

This is how I did it:

ChromeDriverService driverService = ChromeDriverService.createDefaultService();

ChromeDriver driver = new ChromeDriver(driverService, options);

Map<String, Object> commandParams = new HashMap<>();
commandParams.put("cmd", "Page.setDownloadBehavior");

Map<String, String> params = new HashMap<>();
params.put("behavior", "allow");
params.put("downloadPath", downloadFilepathString);
commandParams.put("params", params);

ObjectMapper objectMapper = new ObjectMapper();
HttpClient httpClient = HttpClientBuilder.create().build();

String command = objectMapper.writeValueAsString(commandParams);

String u = driverService.getUrl().toString() + "/session/" + driver.getSessionId() + "/chromium/send_command";

HttpPost request = new HttpPost(u);
request.addHeader("content-type", "application/json");
request.setEntity(new StringEntity(command));
httpClient.execute(request);

Hello,

Any plans to support setDownloadBehavior in order to download files using chrome in headless mode?

Any doc/examples in .Net would be much appreciated.

Regards,
Nicolas

Even I am facing this same issue

Has anyone got this working with Ruby/Rails? Also, can you get a development build of chrome on Heroku?

@p0deje, @jonhkr
I am working on integrating this with the Ruby bindings, but for some reason I am not able to download files in headless mode.

Could you please verify this spec, it is failing for me: https://github.com/pulkitsharma07/selenium/commit/1142e0b5ff859ffea3b5b3fccad23433ad08089f#diff-b1ade8967288d0002579a914345a9f56R72

For me even https://github.com/SeleniumHQ/selenium/blob/master/javascript/node/selenium-webdriver/test/chrome/devtools_test.js#L51 is failing.

I am using Chrome 64 and chromedriver 2.35, not sure what could be causing this.

I've popped an optimisation in, also, whilst not having worked in this area of the specs. Is there a reason you verbosely create the driver, there are helpers such as create_driver! which will check to see if an instance exists and/or create one.

No particular reason, I missed that. The branch is not yet ready, Will do the changes to use create_driver!.

But, still, the main issue is that I am not able to download files in headless mode.

Is there an example for .NET?

@pulkitsharma07 I cannot make it work too, maybe something has changed in recent Chrome/ChromeDriver?

So, in my headless tests Chrome is crashing on clicking the element to download the file, whereas it is not crashing with the same scenario in normal mode.

chromedriver logs:

  [1518706724.071][DEBUG]: DEVTOOLS COMMAND Input.dispatchMouseEvent (id=32) {
     "button": "left",
     "clickCount": 1,
     "modifiers": 0,
     "type": "mouseReleased",
     "x": 20,
     "y": 16
  }
  [1518706724.374][SEVERE]: Unable to receive message from renderer
  [1518706724.374][INFO]: Waiting for pending navigations...
  [1518706724.374][INFO]: Done waiting for pending navigations. Status: disconnected: not connected to DevTools
  [1518706724.374][DEBUG]: DevTools request: http://localhost:12345/json
  [1518706724.375][DEBUG]: DevTools request failed
  [1518706724.375][INFO]: RESPONSE ClickElement

@p0deje I have filed https://bugs.chromium.org/p/chromedriver/issues/detail?id=2270. Please take a look.

Upgrading to Chrome 65/chromedriver 2.36 fixed my crash issue with headless chrome.

After talking with the chromedriver team, the send_command will be moved to a w3c-compliant protocol extension endpoint (https://w3c.github.io/webdriver/webdriver-spec.html#protocol-extensions).

See the issue raised in the chromium bugtracker: https://bugs.chromium.org/p/chromedriver/issues/detail?id=2307

Ruby landed in master using Driver#download_path=, so we might need to refactor it once Chromium changes to W3C compliant commands.

I just realized that my js selenium webdriver test, which was checking the downloads page in the Chrome browser, does not work when I run it in headless mode. From the conversation above, I gather this is not yet supported, correct?

It works for me on Ruby with current master.

Worked for me with Ruby and Capybara:

page.driver.browser.download_path = YOUR_DOWNLOAD_PATH
click_link "Download stuff"

page.driver.browser.download_path

Technically this is available via the selenium-webdriver gem, as of v 3.13.0

Here's the implementation:

          params = {
            'cmd' => 'Page.setDownloadBehavior',
            'params' => {
              'behavior' => 'allow',
              'downloadPath' => path
            }
          }
          @bridge.send_command(params)

@jackkinsella

How we can implement above solution in c# script coz i need to download pdf and validate in chrome headless browser mode..

Please guide me on this..

Thanks in advance,
Elango

Sorry, don't know C#. I just provided the Ruby implementation as a reference to anyone who is facing the same issue and wanted to translate to their own environment.

@jackkinsella @jbustillos I have tried in c# please have a look into,, https://github.com/SeleniumHQ/selenium/issues/6895 , Please let me know if you get any idea on this... :)

Hi All,

Thanks for the solution : It worked for me for HeadlessChrome Browser Even for Local windows as well as in Linux machine , And Also on jenkins server... :)
For below configuration 👍
Selenium java Version : 3.12.0, Chrome Browser version : 72.0.3626, ChromeDriver verison : 2.46 ,Linux Ubuntu 16.4 ,64bit )

ChromeDriverService driverService = ChromeDriverService.createDefaultService();

ChromeDriver driver = new ChromeDriver(driverService, options);

Map commandParams = new HashMap<>();
commandParams.put("cmd", "Page.setDownloadBehavior");

Map params = new HashMap<>();
params.put("behavior", "allow");
params.put("downloadPath", downloadFilepathString);
commandParams.put("params", params);

ObjectMapper objectMapper = new ObjectMapper();
HttpClient httpClient = HttpClientBuilder.create().build();

String command = objectMapper.writeValueAsString(commandParams);

String u = driverService.getUrl().toString() + "/session/" + driver.getSessionId() + "/chromium/send_command";

HttpPost request = new HttpPost(u);
request.addHeader("content-type", "application/json");
request.setEntity(new StringEntity(command));
httpClient.execute(request);

Here is a .NET implementation to enable downloads using headless chrome:

ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.AddArgument("--headless");

ChromeDriver chromeDriver = new ChromeDriver(chromeOptions);

Dictionary<string, object> commandParameters = new Dictionary<string, object>
{
    ["behavior"] = "allow",
    ["downloadPath"] = @"C:\Downloads"
};

chromeDriver.ExecuteChromeCommand("Page.setDownloadBehavior", commandParameters);

This is how I did it:

ChromeDriverService driverService = ChromeDriverService.createDefaultService();

ChromeDriver driver = new ChromeDriver(driverService, options);

Map<String, Object> commandParams = new HashMap<>();
commandParams.put("cmd", "Page.setDownloadBehavior");

Map<String, String> params = new HashMap<>();
params.put("behavior", "allow");
params.put("downloadPath", downloadFilepathString);
commandParams.put("params", params);

ObjectMapper objectMapper = new ObjectMapper();
HttpClient httpClient = HttpClientBuilder.create().build();

String command = objectMapper.writeValueAsString(commandParams);

String u = driverService.getUrl().toString() + "/session/" + driver.getSessionId() + "/chromium/send_command";

HttpPost request = new HttpPost(u);
request.addHeader("content-type", "application/json");
request.setEntity(new StringEntity(command));
httpClient.execute(request);

This fix is not working with my configuration. I'm still unable to download a file in headless mode.

public static WebDriver getChromeDriver() throws IOException {
//Initialize ChromeDriver

    ChromeOptions options = new ChromeOptions();
    Map<String, Object> prefs = new HashMap<>();
    //String downloadFilepath = System.getProperty("user.dir");
    prefs.put("safebrowsing.enabled", false);
    prefs.put("download.default_directory", System.getProperty("user.dir"));
    prefs.put("download.prompt_for_download", false);
    prefs.put("download.directory_upgrade", true);
    prefs.put("cmd", "Page.setDownloadBehavior");
    prefs.put("profile.default_content_settings.popups", 0);
    prefs.put("behavior", "allow");
    options.setExperimentalOption("prefs", prefs);
    options.addArguments("--disable-notifications");
    options.addArguments("--start-maximized");
    options.addArguments("disable-infobars");
    options.addArguments("--disable-gpu");
    options.addArguments("--headless");
    options.addArguments("window-size=1980,1080");
    options.addArguments("--allow-running-insecure-content");
    options.addArguments("--disable-extensions");
    options.addArguments("--no-sandbox");
    options.addArguments("--test-type");
    options.addArguments("--disable-web-security");
    //options.setBinary("C:\\Program Files (x86)\\Google\\Chrome");
    String chromedriverpath_1 = System.getProperty("user.dir");
    String chromedriverpath = chromedriverpath_1+"//chromedriver_2_44.exe";
    //System.out.println(chromedriverpath);
    System.setProperty("webdriver.chrome.driver", chromedriverpath);
    //System.setProperty("user.dir","//chromedriver_2.44.exe");



    ChromeDriverService driverService = ChromeDriverService.createDefaultService();
    ChromeDriver driver = new ChromeDriver(driverService, options);


    ObjectMapper objectMapper = new ObjectMapper();
    HttpClient httpClient = HttpClientBuilder.create().build();
    String command = null;
    try {
        command = objectMapper.writeValueAsString(prefs);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
    }

    String u = driverService.getUrl().toString() + "/session/" + driver.getSessionId() + "/chromium/send_command";
    HttpPost request = new HttpPost(u);
    request.addHeader("content-type", "application/json");
    request.setEntity(new StringEntity(command));
    httpClient.execute(request);
    return driver;
}

@rishi1992 did you found any solution can you please share.

@amishad02 I use the following successfully

var driver = new ChromeDriver(driverService, options);

var param = new Dictionary<string, string> {{"behavior", "allow"}, {"downloadPath", DownloadPath}
};

var cmdParam = new Dictionary<string, object> {{"cmd", "Page.setDownloadBehavior"}, {"params", param}};

var url = driverService.ServiceUrl + "session/" + driver.SessionId + "/chromium/send_command";
var cli = new WebClient {Headers = {[HttpRequestHeader.ContentType] = "application/json"}};
_ = cli.UploadString(url, JsonConvert.SerializeObject(cmdParam));

Anyone had success sending the /session/:sessionId/chromium/send_command command to a headless chrome process that was launched in a selenium grid? This solution seems to work well for a standalone selenium chrome, but I couldn't get it to work when proxied through the selenium grid.

This has worked for me,

// Set the ChromeDriver path
System.out.println("launching Chrome browser");
String chromedriverPath = System.getProperty("user.dir") + "\chromedriver_2_44.exe";
System.setProperty("webdriver.chrome.driver", chromedriverPath);
// Set the download folder path
String downloadPath = System.getProperty("user.dir");
//String downloadPath = "E:\seleniumdownload";
HashMap chromePrefs = new HashMap();
chromePrefs.put("safebrowsing.enabled", "true");
chromePrefs.put("downloadPath", downloadPath); //This is one of the main line to be written
chromePrefs.put("behavior", "allow"); //This is one of the main line to be written
// Initialize Chrome options
ChromeOptions options = new ChromeOptions();
options.addArguments("--disable-notifications");
options.addArguments("--start-maximized");
options.addArguments("disable-infobars");
options.addArguments("--disable-gpu");
options.addArguments("--headless");
options.addArguments("window-size=1980,1080");
options.addArguments("--allow-running-insecure-content");
options.addArguments("--disable-extensions");
options.addArguments("--no-sandbox");
options.addArguments("--ignore-certificate-errors");
// Initialize driver
ChromeDriverService driverService = ChromeDriverService.createDefaultService();
driver = new ChromeDriver(driverService, options);

Map<String, Object> commandParams = new HashMap<>();
commandParams.put("cmd", "Page.setDownloadBehavior");
commandParams.put("params", chromePrefs);

ObjectMapper objectMapper = new ObjectMapper();
HttpClient httpClient = HttpClientBuilder.create().build();

String command = objectMapper.writeValueAsString(commandParams);
String u = driverService.getUrl().toString() + "/session/" + ((RemoteWebDriver) driver).getSessionId() + "/chromium/send_command";

HttpPost request = new HttpPost(u);
request.addHeader("content-type", "application/json");
request.setEntity(new StringEntity(command));
httpClient.execute(request);

Any good solution/workaround in Python ?
I tried many ideas posted here and over internet, but nothing seems to work. I have a scenario when I just click on a download button and file get's downloaded(what is not happening in headless).

Any good solution/workaround in Python ?
I tried many ideas posted here and over internet, but nothing seems to work. I have a scenario when I just click on a download button and file get's downloaded(what is not happening in headless).

Please check, if there is a new tab is opened, when you click on a download link (for example if has target="_blank" attribute). In my case download in headless with above solution doesn't work for downloads in a new tabs. So you can remove target="_blank" attribute by JS or get href and try to download by direct link in the same tab. This works for me.

to enable headless downloads in Python:

from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options

options = Options()
options.headless = True 
driver = Chrome(options=options)
params = {'behavior': 'allow', 'downloadPath': '/path/for/download'}
driver.execute_cdp_cmd('Page.setDownloadBehavior', params)

Thanks cgoldberg - that works fine!

to enable headless downloads in Python:

from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options

options = Options()
options.headless = True 
driver = Chrome(options=options)
params = {'behavior': 'allow', 'downloadPath': '/path/for/download'}
driver.execute_cdp_cmd('Page.setDownloadBehavior', params)

@cgoldberg What is your env? Chrome and chromedriver versions, standalone selenium or Grid?

Thanks

to enable headless downloads in Python:

from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options

options = Options()
options.headless = True 
driver = Chrome(options=options)
params = {'behavior': 'allow', 'downloadPath': '/path/for/download'}
driver.execute_cdp_cmd('Page.setDownloadBehavior', params)

@cgoldberg I am using this similar procedure with selenium-webdriver for Node and it does not work for me. I suspect that it is because in my case a new tab is opened to download file (target "_blank"). This is executed through JavaScript code when I click on a button.

Does this happen to anyone else?

Is there any way to make this permanent? That is, I want it to be enabled by default when a new instance of the browser is opened.

Is there any way to make this permanent? That is, I want it to be enabled by default when a new instance of the browser is opened.

Make use of Hooks

Is there any way to make this permanent? That is, I want it to be enabled by default when a new instance of the browser is opened.

Make use of Hooks

An example I call from my hooks is 👍
private void createChromeBrowser() { WebDriverManager.chromedriver().setup(); String downloadPath = System.getProperty("user.dir"); HashMap<String, Object> chromePrefs = new HashMap<String, Object>(); chromePrefs.put("safebrowsing.enabled", "true"); chromePrefs.put("downloadPath", downloadPath); chromePrefs.put("behavior", "allow"); ChromeOptions options = new ChromeOptions(); options.addArguments("--headless"); options.addArguments("--window-size=1366,768"); options.addArguments("--disable-infobars"); options.addArguments("--incognito"); options.addArguments("--enable-javascript"); options.addArguments("--disable-websecurity"); options.addArguments("--start-maximised"); options.addArguments("--ignore-certificate-errors"); options.addArguments("--disable-popup-blocking"); options.addArguments("--fast-start"); options.addArguments("--javascript-harmony"); options.addArguments("--no-sandbox"); driver = new ChromeDriver(options); }

The command does not seem to be working now in Chrome 74+ (using Proctractor 5.4.2 and node 10.16.0)

browser.driver.sendChromiumCommand('Page.setDownloadBehavior', {
      behavior: 'allow',
      downloadPath: fullDownloadDirectoryPath
    });

Fails with the error

UnsupportedOperationError: POST /session/ac771ec3019f7b4c7191e2c9ae2b2056/chromium/send_command

Any idea why it is failing or how to get headless Chrome downloads working again?

Because the endpoint for that command changed in chromedriver to /session/:sessionId/goog/cdp/execute

@lmtierney many thanks for this. I am still getting a 500 internal server error now if I use that endpoint uri instead?

http://192.168.1.189:54193/wd/hub/session/d47c025d923e19c77aacab20f69809f6/goog/cdp/execute

This is how I did it:

ChromeDriverService driverService = ChromeDriverService.createDefaultService();

ChromeDriver driver = new ChromeDriver(driverService, options);

Map<String, Object> commandParams = new HashMap<>();
commandParams.put("cmd", "Page.setDownloadBehavior");

Map<String, String> params = new HashMap<>();
params.put("behavior", "allow");
params.put("downloadPath", downloadFilepathString);
commandParams.put("params", params);

ObjectMapper objectMapper = new ObjectMapper();
HttpClient httpClient = HttpClientBuilder.create().build();

String command = objectMapper.writeValueAsString(commandParams);

String u = driverService.getUrl().toString() + "/session/" + driver.getSessionId() + "/chromium/send_command";

HttpPost request = new HttpPost(u);
request.addHeader("content-type", "application/json");
request.setEntity(new StringEntity(command));
httpClient.execute(request);

This fix is not working with my configuration. I'm still unable to download a file in headless mode.

I agree. My Chrome version is "78.0.3904.70". This doesnt work for me either. I have the same code

This worked for me:

Environment

ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-darwin18]
Rails 4.2.10
chromedriver: 79.0.3945.36
webdrivers (4.0.1)
capybara (3.24.0)

Other resources

https://stackoverflow.com/questions/48810757/setting-default-download-directory-and-headless-chrome

Implementation

options = Selenium::WebDriver::Chrome::Options.new
  options.add_argument('--window-size=1500,1500')
  options.add_argument('--headless')
  options.add_argument('--no-sandbox')
  options.add_argument('--disable-gpu')
  options.add_argument('--disable-popup-blocking')

  options.add_preference(:download,
                          directory_upgrade: true,
                          prompt_for_download: false,
                          default_directory: ENV['downloads_folder']
                          )

  options.add_preference(:browser, set_download_behavior: { behavior: 'allow' })

Capybara.register_driver :headless_chrome do |app|
  driver = Capybara::Selenium::Driver.new(
                                app,
                                browser: :chrome,
                                options: options
                                )


  bridge = driver.browser.send(:bridge)
  path = '/session/:session_id/chromium/send_command'
  path[':session_id'] = bridge.session_id

  bridge.http.call(:post, path, cmd: 'Page.setDownloadBehavior',
                                params: {
                                  behavior: 'allow',
                                  downloadPath: ENV['downloads_folder'],
                              })

  driver
end

Capybara.javascript_driver = ENV['capybara_js_driver'].to_sym

I'm trying to get this working on javascript using nodejs. Is there a solution yet?

Was this page helpful?
0 / 5 - 0 ratings