How to Upload and Download Files with Selenium in Java
Upload files with Selenium Java using file inputs, then handle local and Grid downloads with reliable completion checks and runnable examples.
To upload a file with Selenium in Java, locate the page’s input[type=file] element and call sendKeys with the file’s absolute path. For downloads, configure the browser’s download directory and wait for a real completion condition. With a remote Selenium Grid, uploads need a LocalFileDetector; downloads need managed downloads enabled and a session created with se:downloadsEnabled=true.
The key distinction is where the browser runs. A local browser and the test process share a filesystem; a Grid browser runs on a node whose files are not automatically available to the test client.
1. Upload a file with local Selenium WebDriver
Selenium does not operate the operating system’s native file chooser. Send the path directly to the HTML file input instead. Use an absolute path to a file that exists where the test process runs.
import java.io.File;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
public class UploadFile {
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
try {
driver.get("https://the-internet.herokuapp.com/upload");
File uploadFile = new File("src/test/resources/document.pdf");
if (!uploadFile.isFile()) {
throw new IllegalArgumentException("Upload file does not exist: " + uploadFile.getAbsolutePath());
}
WebElement fileInput = driver.findElement(By.cssSelector("input[type='file']"));
fileInput.sendKeys(uploadFile.getAbsolutePath());
driver.findElement(By.id("file-submit")).click();
String uploadedName = driver.findElement(By.id("uploaded-files")).getText();
if (!uploadFile.getName().equals(uploadedName)) {
throw new AssertionError("Expected " + uploadFile.getName() + " but got " + uploadedName);
}
} finally {
driver.quit();
}
}
}
The example uses Selenium’s documented file-input pattern and a public demonstration form. For a real application, replace the URL and success assertion with the application’s upload page and its confirmation state. Selenium’s file upload documentation describes this approach.
When the file input is hidden or customized
Custom upload buttons often open the operating system chooser after a click, but the page still typically contains an actual file input. Locate that input and send it the path. Do not try to automate the native chooser with keyboard or mouse actions. If the page has multiple file inputs, select the one associated with the intended form or section.
For a multi-file input supported by the page, send absolute paths separated by a newline:
fileInput.sendKeys(fileOne.getAbsolutePath() + "\n" + fileTwo.getAbsolutePath());
Only use this when the HTML input permits multiple files and the application accepts them. For a single-file control, upload each file through the page’s intended workflow.
2. Upload to a browser on Selenium Grid
A path on the test client does not automatically exist on a remote browser node. Configure Java’s RemoteWebDriver with LocalFileDetector before sending the path. Selenium then transfers the local file for the remote session.
import java.io.File;
import java.net.URL;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.LocalFileDetector;
import org.openqa.selenium.remote.RemoteWebDriver;
public class RemoteUpload {
public static void main(String[] args) throws Exception {
RemoteWebDriver driver = new RemoteWebDriver(
new URL("http://localhost:4444"),
new org.openqa.selenium.chrome.ChromeOptions()
);
try {
driver.setFileDetector(new LocalFileDetector());
driver.get("https://the-internet.herokuapp.com/upload");
File uploadFile = new File("src/test/resources/document.pdf");
if (!uploadFile.isFile()) {
throw new IllegalArgumentException("Upload file does not exist: " + uploadFile.getAbsolutePath());
}
WebElement fileInput = driver.findElement(By.cssSelector("input[type='file']"));
fileInput.sendKeys(uploadFile.getAbsolutePath());
driver.findElement(By.id("file-submit")).click();
String uploadedName = driver.findElement(By.id("uploaded-files")).getText();
if (!uploadFile.getName().equals(uploadedName)) {
throw new AssertionError("Upload was not confirmed: " + uploadedName);
}
} finally {
driver.quit();
}
}
}
Use a Grid URL and browser options appropriate to your environment. If your test framework creates the remote driver, set the file detector on that driver before the upload step. Selenium documents this behavior in its Remote WebDriver guide.
3. Download with a local browser
Set the browser’s download directory to a known location, trigger the download, then wait until the file exists and is no longer being written. ChromeDriver does not automatically wait for a download to finish. Avoid a fixed sleep: slow machines, large files, and network variation make it unreliable.
Configure Chrome’s download directory
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
Path downloadDir = Path.of("target", "downloads").toAbsolutePath();
java.nio.file.Files.createDirectories(downloadDir);
Map<String, Object> prefs = new HashMap<>();
prefs.put("download.default_directory", downloadDir.toString());
prefs.put("download.prompt_for_download", false);
prefs.put("download.directory_upgrade", true);
ChromeOptions options = new ChromeOptions();
options.setExperimentalOption("prefs", prefs);
ChromeDriver driver = new ChromeDriver(options);
try {
driver.get("https://example.com/export");
driver.findElement(By.id("download-report")).click();
// Wait for the expected file using the condition-based helper below.
} finally {
driver.quit();
}
The directory should be writable by the process running Chrome. Use an absolute path because browser processes may have a different working directory than the test runner.
Wait for a completed local download
For Chrome, a temporary .crdownload file commonly indicates an in-progress download. A robust wait checks that the expected file exists, has a nonzero size when appropriate, and has a stable size across consecutive checks. If the application exposes an export-ready status or completion event, prefer that signal and then verify the file.
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
static Path waitForStableFile(Path directory, String fileName, Duration timeout) throws Exception {
Path expected = directory.resolve(fileName);
long deadline = System.nanoTime() + timeout.toNanos();
long previousSize = -1;
int stableChecks = 0;
while (System.nanoTime() < deadline) {
boolean partialDownload = false;
try (var files = Files.list(directory)) {
partialDownload = files.anyMatch(path -> path.getFileName().toString().endsWith(".crdownload"));
}
if (Files.isRegularFile(expected) && !partialDownload) {
long size = Files.size(expected);
if (size > 0 && size == previousSize) {
stableChecks++;
if (stableChecks >= 2) return expected;
} else {
stableChecks = 0;
}
previousSize = size;
}
Thread.sleep(250); // short polling interval; timeout is condition-based
}
throw new java.util.concurrent.TimeoutException("Download did not complete: " + expected);
}
Adapt the partial-file check to the browser and file types in use; the marker is browser-specific. A zero-byte file may be valid for some workflows, so adjust the size assertion if an empty file is expected. Make sure the destination is unique per test or clean it before starting, otherwise an old file can satisfy the existence check.
4. Download from Selenium Grid to the test client
A remote browser’s download directory is on the Grid node. It is not the client’s local download directory. For Grid managed downloads, enable the feature on the node and request the session capability se:downloadsEnabled=true. Selenium lists Chrome, Edge, and Firefox as supported browsers for managed downloads.
Enable managed downloads on Grid
Start the node or standalone Grid with managed downloads enabled:
java -jar selenium-server.jar standalone --enable-managed-downloads true
For a distributed Grid, apply the managed-download option to the relevant node configuration. The Grid must be configured to support downloads before the browser session begins.
Create the session with downloads enabled and retrieve the file
import java.nio.file.Path;
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.remote.Augmenter;
import org.openqa.selenium.HasDownloads;
ChromeOptions options = new ChromeOptions();
options.setCapability("se:downloadsEnabled", true);
RemoteWebDriver rawDriver = new RemoteWebDriver(gridUrl, options);
// Augment where required by the Selenium version and remote browser feature.
RemoteWebDriver driver = (RemoteWebDriver) new Augmenter().augment(rawDriver);
try {
driver.get("https://example.com/export");
driver.findElement(By.id("download-report")).click();
HasDownloads downloads = (HasDownloads) driver;
String expectedName = "report.csv";
Path destination = Path.of("target", "grid-downloads").toAbsolutePath();
java.nio.file.Files.createDirectories(destination);
// Poll because the downloadable-file listing is only a snapshot.
long deadline = System.nanoTime() + Duration.ofSeconds(60).toNanos();
while (System.nanoTime() < deadline && !downloads.getDownloadableFiles().contains(expectedName)) {
Thread.sleep(250);
}
if (!downloads.getDownloadableFiles().contains(expectedName)) {
throw new java.util.concurrent.TimeoutException("Grid download not listed: " + expectedName);
}
downloads.downloadFile(expectedName, destination);
Path downloaded = destination.resolve(expectedName);
if (!java.nio.file.Files.isRegularFile(downloaded)) {
throw new AssertionError("File was not retrieved to client: " + downloaded);
}
// Remove files from the managed download area when appropriate.
downloads.deleteDownloadableFiles();
} finally {
driver.quit();
}
Use the API exposed by your Selenium Java version. Selenium’s remote documentation describes HasDownloads, getDownloadableFiles(), downloadFile(name, targetDirectory), and deleteDownloadableFiles(); it also notes that Augmenter may be needed for remote browser features. RemoteWebDriverBuilder automatically augments. The Java interfaces evolve, so confirm the API in the Selenium version pinned by your project. See the Remote WebDriver page and Grid CLI options.
The listing is an immediate snapshot, not a completion guarantee. For reliable tests, wait for an application-level completion signal when available, then poll the managed listing and validate the retrieved file’s name and content. Grid cleans up managed download files when the session ends or times out.
5. Choose local downloads or Grid managed downloads
| Concern | Local browser | Remote Selenium Grid |
|---|---|---|
| Upload | Send the full path to the file input. | Set LocalFileDetector, then send the client path. |
| Download location | Configured directory is on the test machine. | Browser writes on the remote node; use managed download retrieval to bring it to the client. |
| Setup | Set browser download preferences as needed. | Enable managed downloads on Grid and request se:downloadsEnabled. |
| Completion | ChromeDriver does not wait for completion. | File listing is a snapshot. Poll or use an app-level completion signal. |
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
InvalidArgumentException or file input rejects the path |
Path is relative, missing, unreadable, or points to a directory. | Resolve it to an absolute path and check isFile() in the test process before sending it. |
| Upload works locally but fails on Grid | The remote node cannot see a path that exists only on the client. | Set LocalFileDetector on the Java RemoteWebDriver before calling sendKeys. |
| Element not found for upload | Wrong selector, iframe, delayed page, or multiple file inputs. | Wait for the page state, switch to the correct frame if applicable, and inspect the actual file input associated with the form. |
| Native file dialog appears | The test clicked a custom button instead of sending a path to the HTML input. | Locate the underlying input[type=file] and use sendKeys. |
| Downloaded file is missing locally after Grid run | It was saved on the node, not the client. | Enable managed downloads on Grid, create the session with se:downloadsEnabled=true, then retrieve with HasDownloads. |
| Download listing is empty immediately after click | The download has not completed; the listing is only a snapshot. | Wait on a meaningful completion condition and poll the listing with a timeout. |
| Download wait passes using an old file | Test reused a directory containing a previous result. | Use a unique directory per test or delete the expected output before triggering the download. |
| Grid rejects or ignores managed downloads | Node was not started with the feature enabled, capability was omitted, or browser/client version lacks the required support. | Enable --enable-managed-downloads true, set the session capability, and verify supported browser and Selenium binding versions. |
| Downloaded file exists but is incomplete or invalid | Existence was checked before writing completed, or the server returned an error document. | Wait for completion, then assert expected size or parse/check file contents and response semantics. |
7. Reliability, performance, and cost
- Reliability: Prefer application completion signals over fixed sleeps. Use bounded polling with a clear timeout and a unique directory. Check the file’s expected name and, when useful, its content or format.
- Performance: Uploading transfers the file to the browser context; remote uploads add a client-to-node transfer. Keep test fixtures reasonably sized and avoid repeating the same large transfer when the test can safely reuse prepared data.
- Cleanup: Close the driver in a
finallyblock. Remove local temporary files after assertions. Grid managed downloads can be deleted with the supported API and are also cleaned up at session end or timeout. - Cost: Selenium itself is the browser automation workflow described here. Grid infrastructure and any hosted browser service have their own costs; the research sources do not establish a universal price or performance figure.
8. Or skip the browser setup
If the goal is a screenshot or PDF of a page rather than exercising the page’s upload/download workflow, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns an image or PDF. It does not replace a test that must verify a form upload or downloaded file.
For the browser-based capture, cookie and consent banners are accepted like a visitor and more than 60 known consent platforms, newsletter popups, and chat widgets are removed before the shot. Each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; response headers report the page verdict and billing status. An MCP server offers take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots monthly with no card; paid plans start at $5 for 3,000. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
Get 1,000 free screenshots a month with no card.
9. FAQ
Can Selenium upload a file without opening the system file picker?
Yes. Send the full path to the HTML file input with sendKeys; Selenium’s documented approach avoids controlling the native chooser.
Why can Grid upload a file from my laptop?
LocalFileDetector transfers the test client’s file so the remote node can use it. Without it, the node may interpret the path as a location on its own filesystem.
Does Selenium automatically wait for downloads?
No. ChromeDriver does not automatically wait for completion, and Grid’s downloadable-file listing is a snapshot. Wait on an application or filesystem condition.
Can I access a Grid download after the session ends?
Managed downloads are cleaned up when the session ends or times out. Retrieve needed files before quitting the session.


