skip to Main Content

I’m trying to use Node to capture a screenshot of the user’s screen on all displays and save everything in just one image, so far everything works fine if I call it the first time, but if I try to call it a second time without restarting the server, An error appears saying that the file cannot be opened.

Because I wanted to be able to make several requests to the screenshot, any ideas what I can do?
I’m still integrating with Node and JS so I don’t know how it works very well.

Code:

const express = require('express');
const app = express();
const port = 3000;
const fs = require('fs');


app.get('/', (req, res) => {
  hello = 'Hello World!';
  console.log(hello);
  res.send(hello);
});

app.get('/screenshot', (req, res) => {
  const screenshot = require('screenshot-desktop');
  const sharp = require('sharp');

  screenshot.listDisplays().then((displays) => {
    let screenshots = [];
    displays.forEach((display, i) => {
      const screenshotPath = `screenshot${i}.png`;
      screenshot({ screen: display.id }).then((imgBuffer) => { 
        fs.writeFile(screenshotPath, imgBuffer, (err) => { 
          if (err) throw err;
          console.log('Screenshot salvo!');
          screenshots.push(screenshotPath);
          if (screenshots.length === displays.length) {
            // Todas as screenshots foram salvas, agora junte-as
            Promise.all([
              sharp(screenshots[1]).metadata(),
              sharp(screenshots[0]).metadata()
            ]).then(([metadata1, metadata2]) => {
              sharp(screenshots[1])
                .extend({
                  top: 0,
                  bottom: 0,
                  left: 0,
                  right: metadata2.width,
                  background: { r: 0, g: 0, b: 0, alpha: 0 }
                })
                .composite([{ input: screenshots[0], left: metadata1.width, top: 0 }])
                .toBuffer()
                .then((buffer) => {
                  let base64Image = `data:image/png;base64,${buffer.toString('base64')}`;
                  res.send(base64Image);
                })
                .catch((err) => console.error(err));
            }).catch(err => console.error(err));
          }
        });
      }, (err) => {
        console.error(err);
      });
    });
  });
});

app.listen(port, () => {
  console.log(`Servidor rodando em http://localhost:${port}`);
});

Error im getting and Log:

Servidor rodando em http://localhost:3000
Screenshot salvo!
Screenshot salvo!
F:Andresk-access-control-aifacialRecogserver.js:23
          if (err) throw err;
                   ^

[Error: UNKNOWN: unknown error, open 'facialRecogscreenshot0.png'] {
  errno: -4094,
  code: 'UNKNOWN',
  syscall: 'open',
  path: 'facialRecog\screenshot0.png'
}

Node.js v20.12.2

I tried to create it like this, but instead of replacing the file, I would delete it and create a new one, but it would give me the error error eperm operation not permitted unlink saying that I can’t delete a file, but I’m not using it nowhere, and I can only delete it when I close the localhost:3000 server

2

Answers


  1. I rewrite your code used async/await and it worked as expect. But it still has some issue to fixed.
    (1) when user only has one display and screenshots[1] will get index out of bound error.
    (2) file name with screenshot${i}.png will duplicate when user call api concurrently.

    app.get('/screenshot', async (req, res) => {
      try {
        const displays = await screenshot.listDisplays();
        let screenshots = [];
        for (let i = 0; i < displays.length; i++) {
          const display = displays[i];
          const screenshotPath = `screenshot${i}.png`;
          const imgBuffer = await screenshot({ screen: display.id });
          fs.writeFileSync(screenshotPath, imgBuffer);
          console.log('Screenshot salvo!');
          screenshots.push(screenshotPath);
        }
        if (screenshots.length === displays.length) {
          // Todas as screenshots foram salvas, agora junte-as
          const [metadata1, metadata2] = await Promise.all([
            sharp(screenshots[1]).metadata(),
            sharp(screenshots[0]).metadata()
          ]);
    
          const buffer = await sharp(screenshots[1])
            .extend({
              top: 0,
              bottom: 0,
              left: 0,
              right: metadata2.width,
              background: { r: 0, g: 0, b: 0, alpha: 0 }
            })
            .composite([{ input: screenshots[0], left: metadata1.width, top: 0 }])
            .toBuffer();
    
          // unlink after merge image
          for (let i = 0; i < screenshots.length; i++) {
            fs.unlinkSync(screenshots[i]);
          }
    
          let base64Image = `data:image/png;base64,${buffer.toString('base64')}`;
          res.send(base64Image);
        }
        throw new Error('screenshots.length !== displays.length');
      } catch (err) {
        console.error(err);
        res.send(err.message);
      }
    });
    
    Login or Signup to reply.
  2. The error you’re encountering suggests that the file you’re trying to open or write to (screenshot0.png) is being locked or is still in use when the second request to your screenshot endpoint is made. This is a common issue when working with file I/O operations where the file is not properly released or is still being processed when subsequent attempts to access it are made.
    Here are a few approaches to resolve this issue:

    1. Use Unique File Names for Each Screenshot

    Instead of writing to the same file name every time, you can generate unique file names based on timestamps or UUIDs. This prevents conflicts with files being locked or still in use.

    const { v4: uuidv4 } = require('uuid');
    
    displays.forEach((display, i) => {
      const screenshotPath = `screenshot${i}-${uuidv4()}.png`;
      // rest of the code...
    });
    

    2. Checking File Access Before Writing

    fs.access(screenshotPath, fs.constants.F_OK | fs.constants.W_OK, (err) => 
    {
     if (err) {
      console.error('File is not accessible:', screenshotPath);
      return; // Handle error or retry logic
     }
      // File write operation here
    });
    

    3. Proper Error Handling

    if (err) {
     console.error('Error writing file:', err);
     res.status(500).send('Error processing your request');
     return;
    }
    

    For an example:

        const express = require('express');
        const app = express();
        const port = 3000;
        const fs = require('fs');
    const screenshot = require('screenshot-desktop');
    const sharp = require('sharp');
    
    app.get('/', (req, res) => {
      const hello = 'Hello World!'; // Ensure variables are properly declared with `const` or `let`
      console.log(hello);
      res.send(hello);
    });
    
    app.get('/screenshot', (req, res) => {
      screenshot.listDisplays().then((displays) => {
        const screenshots = [];
        const promises = displays.map((display, i) => {
          const screenshotPath = `screenshot${i}.png`;
          return screenshot({ screen: display.id })
            .then(imgBuffer => {
              return fs.promises.writeFile(screenshotPath, imgBuffer);
            })
            .then(() => {
              console.log('Screenshot saved:', screenshotPath);
              screenshots.push(screenshotPath);
              return screenshotPath; // Return path for further processing
            });
        });
    
        Promise.all(promises)
          .then(() => {
            // All screenshots have been saved, now stitch them together
            return Promise.all(screenshots.map(path => sharp(path).metadata().then(metadata => ({ path, metadata }))));
          })
          .then(files => {
            const totalWidth = files.reduce((sum, file) => sum + file.metadata.width, 0);
            return sharp({
              create: {
                width: totalWidth,
                height: files[0].metadata.height,
                channels: 4,
                background: { r: 0, g: 0, b: 0, alpha: 0 }
              }
            })
            .composite(files.map((file, i) => ({
              input: file.path,
              left: i > 0 ? files[i - 1].metadata.width : 0,
              top: 0
            })))
            .toBuffer();
          })
          .then(buffer => {
            const base64Image = `data:image/png;base64,${buffer.toString('base64')}`;
            res.send(base64Image);
          })
          .catch(err => {
            console.error(err);
            res.status(500).send('Error processing screenshots');
          });
      }).catch(err => {
        console.error(err);
        res.status(500).send('Error listing displays');
      });
    });
    
    app.listen(port, () => {
      console.log(`Server running at http://localhost:${port}`);
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search