Uploading files to sites
When automating browser tasks, you may need to upload files to websites. This guide demonstrates how to upload files to sites using Puppeteer with Browserless, including a workaround for uploading client-side data.
Understanding file uploads with Puppeteer
By design, Puppeteer's Page.uploadFile()
method uploads files from the server's filesystem, not from the client. This means the file must exist on the server where your Puppeteer script is running. However, there's a workaround for uploading client-side data by creating virtual files using Page.evaluate()
with DataTransfer
objects.
Example: Uploading client-side data to a compression site
The following example shows how to upload file data (as a Uint8Array) to jimpl.com using a virtual file creation workaround:
import puppeteer from "puppeteer-core";
import path from "path";
import fs from "fs";
const sleep = (ms) => new Promise((res) => setTimeout(res, ms));
const API_TOKEN = "YOUR_API_TOKEN_HERE";
const BROWSER_WS_ENDPOINT = `wss://production-sfo.browserless.io?token=${API_TOKEN}`;
const fileToUpload = {
name: "image.png",
content: fs
.readFileSync(path.join(process.cwd(), "image.png"))
.toString("base64"),
mimeType: "image/png",
};
const uploadFile = async (page, fileToUpload) => {
await page.evaluate(
({ selector, fileName, mimeType, base64Data }) => {
function b64ToUint8Array(b64) {
const binary = atob(b64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return bytes;
}
const input = document.querySelector(selector);
const file = new File([b64ToUint8Array(base64Data)], fileName, {
type: mimeType,
});
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
input.files = dataTransfer.files;
const event = new Event("change", { bubbles: true });
input.dispatchEvent(event);
},
{
selector: "input[type=file]",
fileName: fileToUpload.name,
mimeType: fileToUpload.mimeType,
base64Data: fileToUpload.content,
}
);
};
(async () => {
const browser = await puppeteer.connect({
browserWSEndpoint: BROWSER_WS_ENDPOINT,
});
const page = await browser.newPage();
console.log("Navigating to jimpl.com...");
await page.goto("https://jimpl.com/");
console.log("Waiting for file input...");
await page.waitForSelector("input[type=file]");
await uploadFile(page, fileToUpload);
console.log("Waiting 5 seconds to allow upload...");
await sleep(5000);
await page.screenshot({ path: "imagecompressor_upload_result.png" });
console.log("Screenshot taken: imagecompressor_upload_result.png");
await browser.close();
})().catch((e) => {
console.error("Error:", e);
});
How it works
- Connect to Browserless: The script connects to Browserless using the WebSocket endpoint.
- Prepare file data: File content is read and converted to base64 for transfer to the browser context.
- Navigate to the target site: Opens the file upload website.
- Locate the file input: Waits for the file input element to be available.
- Create virtual file: Uses
page.evaluate()
to run code in the browser context that:- Converts base64 data back to a Uint8Array
- Creates a File object with the data and metadata
- Creates a DataTransfer object and adds the file
- Assigns the files to the input element
- Triggers a change event to notify the site
- Wait for processing: Allows time for the upload to complete.
- Verify the result: Takes a screenshot to confirm the upload was successful.
- Clean up: Closes the browser session.
Tips for file uploads
- File formats: Make sure to set the correct
mimeType
for your file data. - Event triggering: Always trigger a
change
event after setting the files to ensure the website detects the upload. - Error handling: Some sites may require clicking additional buttons or filling out forms after file selection.
- Wait times: Allow sufficient time for uploads to process before taking further actions.