Search code examples
node.jsplaywright

Playwright - Can't access local files using file:// in an img tag


In node js, I am using playwright to open a browser page, setcontent with some html and then take its screenshot. The html contains an image tag and the src of the img is a local file. In the screenshot I don't see the local image. Is local file access disabled? if so how to enable it? I have used relative and absolute path of the file name. If I use a web address, the image is displayed. I am using Mac OS.

async function screenShot() {
  
     const browser = await chromium.launch();
    const context = await browser.newContext();
    const page = await context.newPage();
    await page.setViewportSize({ width:200, height:200 });
    await page.setContent(`
<html><body>
        <div id="container">
        <h1>HELLO</h1>
                <img  src="file:///resources/image.png" />
            </div></body>
            </html>
    `)

    await page.locator("#container").screenshot({ path: 'screenshot.png' });

    // Teardown
    await context.close();
    await browser.close();
}

Output Image -

screenshot


Solution

  • The browser can't read from the file system like this for security reasons. There are flags to disable it, but I'd use those as a last resort.

    One option is to run a web server to host the site and serve the image and other static assets. This can be done out of the box with Python, or using PHP or the http-server Node package. Many IDEs run servers for you when you launch a web project.

    For Python, move your HTML to index.html and run python3 -m http.server from the directory with index.html in it, verify the image loads when visiting http://localhost:8000 in your browser, then use page.goto("http://localhost:8000") and automate the site as normal.

    An alternative is to inject the image as base64 into the raw HTML, bypassing the browser request. Here's a minimal example:

    const {chromium} = require("playwright"); // ^1.38.0
    const fs = require("node:fs/promises");
    
    let browser;
    (async () => {
      const b64 = await fs.readFile("test.png", {encoding: "base64"});
      browser = await chromium.launch({headless: false});
      const page = await browser.newPage();
      await page.setContent(`<!DOCTYPE html>
        <html>
        <body>
          <h1>hello world</h1>
          <img src="data:image/png;base64,${b64}" alt="hello world">
        </body>
        </html>
      `);
      await page.screenshot({path: "test_out.png", fullPage: true});
    })()
      .catch(err => console.error(err))
      .finally(() => browser?.close())