loading...
Cover image for Practical Puppeteer: How to upload a file programatically

Practical Puppeteer: How to upload a file programatically

sonyarianto profile image Sony AK ・3 min read

Hi everybody! Today I will share about how to upload file using Puppeteer. If you don't know about Puppeteer yet, here is the brief explanation.

Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium. Go to https://pptr.dev for more details.

During automation using Puppeteer sometimes we want to simulate upload a file and now I will show it with real scenario.

The scenario is we will upload a file to website called Easyupload.io at https://easyupload.io. We can upload a file without need for login and they will by default keep the file for 7 days and they will give us the URL of uploaded file. Quite simple and perfect sample.

Alt Text Easyupload.io, the website for our scenario

Let's start.

Preparation

Install Puppeteer

npm i puppeteer

The API to upload file is elementHandle.uploadFile(...filePaths). We will prepare the file to upload called test_to_upload.jpg and pass to uploadFile method.

The code

const puppeteer = require('puppeteer');

(async () => {
    // set some options (set headless to false so we can see 
    // this automated browsing experience)
    let launchOptions = { headless: false, args: ['--start-maximized'] };

    const browser = await puppeteer.launch(launchOptions);
    const page = await browser.newPage();

    // set viewport and user agent (just in case for nice viewing)
    await page.setViewport({width: 1366, height: 768});
    await page.setUserAgent('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36');

    // go to the target web
    await page.goto('https://easyupload.io/');

    // get the selector input type=file (for upload file)
    await page.waitForSelector('input[type=file]');
    await page.waitFor(1000);

    // get the ElementHandle of the selector above
    const inputUploadHandle = await page.$('input[type=file]');

    // prepare file to upload, I'm using test_to_upload.jpg file on same directory as this script
    // Photo by Ave Calvar Martinez from Pexels https://www.pexels.com/photo/lighthouse-3361704/
    let fileToUpload = 'test_to_upload.jpg';

    // Sets the value of the file input to fileToUpload
    inputUploadHandle.uploadFile(fileToUpload);

    // doing click on button to trigger upload file
    await page.waitForSelector('#upload');
    await page.evaluate(() => document.getElementById('upload').click());

    // wait for selector that contains the uploaded file URL
    await page.waitForSelector('#upload-link');
    await page.waitFor(5000);

    // get the download URL
    let downloadUrl = await page.evaluate(() => {
        return document.getElementById('upload-link').value;
    });

    // display the result on console
    console.log({'file': fileToUpload,
                 'download_url': downloadUrl});

    // close the browser
    await browser.close();
})();

The code is full of comment, I hope you can understand. I set the headless option to false so we can see the browser in action.

In the code I also put some page.waitFor() to avoid race condition during scraping this scenario.

Run it

node upload_file.js

If everyting OK it will display the result in console similar like below.

{
  file: 'test_to_upload.jpg',
  download_url: 'https://easyupload.io/ffbvzk'
}

You can go to the download_url above and you will get your uploaded image. It means our upload automation with Puppeteer works perfectly. I have test it using headless mode and headful mode, all are working well.

That's it. Thank you and I hope you enjoy it.

Source code of this sample is available at GitHub https://github.com/sonyarianto/upload-file-with-puppeteer.git.

Reference

Credits

Posted on by:

Discussion

markdown guide
 

can you please write an article about, how to, make a puppeteer extra plugin?

I want to make a plugin so that, whenever any interactions (click on a button, navigate to a page, etc..) it will take a screenshot automatically before and after the interaction. so that I don't have to call page.screenshot manually every time!

 

Hi Ashiqur, wow nice use case, I still don't have any experience for writing plugin for Puppetter but I will note it for this nice use case.

 

hey, i am starting a new project with puppeteer. Do you want to work ?

 

Thanks Sony, Great article!
Can you please look into this issue. i'm struggling with it and stuck for days now.

github.com/puppeteer/puppeteer/iss...

It is about uploading an image
Thanks in advance.

 

Hi Harshajayaweera, sorry for the late reply, I think I miss this, already got solution for your problem?

 

This is great, except I'm stuck at one part. What would you do if there is no upload button, and the upload is normally automatically trigerred when the file is uploaded? For some reason nothing happens when I call the uploadFile function, I would expect it to continue automatically.

 

Hi Chris,
Thanks, but sorry I am still don't understand your use case, do you have any sample of code or target web to do that? What do you mean by "no upload button". Basically we just target the <input type="file"> and call the uploadFile method for the file input selector. Tell me more and maybe I can help you.

 

Hi Sony, first of all, thanks for a very useful post. This works perfect on my Mac. But when I deploy the code on Heroku, the file doesn't really upload. It looks like Pupeteer on Heroku couldn't find the file in the following line, thus the submit resulting in no file upload:

inputUploadHandle.uploadFile(fileToUpload);

The file exists in that path on Heroku's server because I'm able to send that file as an email attachment using Node Mailer. I'v been scratching my head for two days in a row. I wonder if you happen to have tried this on a Heroku app.

 

Hi Oliver, sorry for super late reply, maybe file path is wrong? or try by using full path to the file.