当前位置: 首页 > 面试题库 >

如何使Puppeteer在客户端使用ReactJS应用程序

卜昂熙
2023-03-14
问题内容

我对React还是很陌生,我正在开发一个应用程序,它将获取网页的实际屏幕截图,并且该应用程序可以在所截取的屏幕截图之上绘制并添加涂鸦。最初,我使用html2canvas和domToImage拍摄客户端屏幕快照,但它无法完全呈现网页中显示的图像。

Reddit用户/
pamblam0建议我调查Google的Puppeteer。它的工作方式是启动无头铬浏览器,该浏览器转到我在本地主机上的react应用,然后轻松获取整个页面的屏幕截图。但是我的问题是,木偶戏在React应用程序中不能很好地发挥作用。它给了我一个ws错误,正如在Google搜索中所述,可以通过简单地安装ws来解决(这是行不通的)。

现在,我的操纵my脚本可以完成我的react应用程序。据我了解,它不适用于客户端应用程序(我可能错了)。我想发生的是,每当我单击react应用程序中的按钮时,puppeteer应该执行并返回base64字符串,然后将其传递到我的react应用程序中的组件。

到目前为止,这是我所做的。

puppeteerApp.js

const puppeteer = require('puppeteer');

const takeScreenshot = async () => {
    puppeteer.launch().then(async browser => {
        const page = await browser.newPage();
        const options = {
            path: 'saved_images/webshot.png',
            encoding: 'base64'
        }
        await page.goto('http://localhost:3000/', { waitUntil: 'networkidle2' });
        const elem = await page.$('iframe').then(async (iframe) => {
            return await iframe.screenshot(options)
        });

        await browser.close()
    });
}

takeScreenshot();

来自React应用的代码 App.js

import React, { Component } from 'react';
import ScreenshotsContainer from './containers/ScreenshotsContainer/ScreenshotsContainer'
import ImageContainer from './containers/ImageContainer/ImageContainer';
import html2canvas from 'html2canvas';
import domtoimage from 'dom-to-image';
import Button from './components/UI/Button/Button'
import classes from './App.module.css';
import { CSSTransition } from 'react-transition-group'
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';


class App extends Component {

  constructor(props) {
    super(props);
    this.state = {
      imgURIArray: [],
      img: null,
      showImageContainer: false,
      showScreenshotContainer: false,
      selectedImageURI: null,
      showSaveAnimation: false,
      showNotify: false
    }
  }


  storeImageToArrayHandler = (imgURI) => {
    if (imgURI !== "") {
      this.setState({ imgURIArray: [...this.state.imgURIArray, imgURI] }, () => {
        this.setState({ showImageContainer: !this.state.showImageContainer })
      })
    }
  }

  getScreenshotHandler = () => {
   //use puppeteer here!!!
  }



  getSelectedImageFromContainerHandler(selectedImageURI) {
    this.setState({
      selectedImageURI: selectedImageURI,
      showImageContainer: !this.state.showImageContainer
    })

  }

  showImageContainerHandler(showImageContainer) {
    this.setState({ showImageContainer: showImageContainer })
  }

  showScreenshotContainerHandler = () => {
    this.setState({ showScreenshotContainer: !this.state.showScreenshotContainer })
  }
  notify = (submitSuccessful, msg) => {
    let message = msg ? msg : ""
    submitSuccessful ?
      toast.success(message, {
        autoClose: 3000,
        position: toast.POSITION.TOP_CENTER
      })
      :
      toast.error(message, {
        position: toast.POSITION.TOP_CENTER
      });

  }
  render() {
    let buttonOps = (
      <CSSTransition
        in={!this.state.showScreenshotContainer}
        appear={true}
        timeout={300}
        classNames="fade"
      >
        <div className={classes.optionButtons}>
          <Button icon={"fas fa-camera"} type={"button-success"} gridClass={""} buttonName={""} style={{ width: "100%", height: "70px" }} onClick={() => this.getScreenshotHandler} />
          <Button icon={"fas fa-images"} type={"button-primary "} gridClass={""} buttonName={""} style={{ width: "100%", height: "70px" }} onClick={() => this.showScreenshotContainerHandler} />
        </div>
      </CSSTransition>
    )

    return (
      <div>
        {
          this.state.showImageContainer ?
            <div>
              <ImageContainer
                img={this.state.img}
                showImageContainer={showImageContainer => this.showImageContainerHandler(showImageContainer)}
                storeImageToArrayHandler={imgURI => this.storeImageToArrayHandler(imgURI)}
                notify={(submitSuccessful, msg) => this.notify(submitSuccessful, msg)}
              />
            </div>
            : null
        }
        <CSSTransition
          in={this.state.showScreenshotContainer}
          appear={true}
          timeout={300}
          classNames="slide"
          unmountOnExit
          onExited={() => {
            this.setState({ showScreenshotContainer: false })
          }}
        >
          <ScreenshotsContainer
            imgURIArray={this.state.imgURIArray}
            getSelectedImageFromContainerHandler={imgURI => this.getSelectedImageFromContainerHandler(imgURI)}
            showScreenshotContainerHandler={() => this.showScreenshotContainerHandler}
            notify={(submitSuccessful, msg) => this.notify(submitSuccessful, msg)}
          />

        </CSSTransition>
        {this.state.showImageContainer ? null : buttonOps}
        {/* <button onClick={this.notify}>Notify !</button> */}
        <ToastContainer />

      </div >
    );
  }
}

export default App;

任何帮助,将不胜感激。谢谢!


问题答案:

您的React.js应用程序在客户端(在浏览器中)运行。由于无法在浏览器中启动完整的浏览器,因此Puppeteer无法在该环境中运行。

您需要的是一台为您完成任务的服务器。您可以提供一个HTTP端点(选项1)或公开您的伪造Websocket(选项2):

选项1:提供HTTP端点

对于此选项,您可以设置服务器来处理传入的请求并为您运行任务(制作屏幕截图):

server.js

const puppeteer = require('puppeteer');
const express = require('express');

const app = express();

app.get('/screenshot', async (req, res) => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto(req.query.url); // URL is given by the "user" (your client-side application)
    const screenshotBuffer = await page.screenshot();

    // Respond with the image
    res.writeHead(200, {
        'Content-Type': 'image/png',
        'Content-Length': screenshotBuffer.length
    });
    res.end(screenshotBuffer);

    await browser.close();
})

app.listen(4000);

使用启动应用程序,node server.js您现在可以将URL传递到服务器,并从服务器获取屏幕截图:http://localhost:4000/screenshot?url=https://example.com/

来自服务器的响应然后可以用作应用程序中图像元素的源。

选项2:将伪造的Websocket公开给客户端

您还可以通过公开Websocket从客户端控制浏览器(在服务器上运行)。

为此,您需要像这样公开服务器的Websocket:

server.js

const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch();
    const browserWSEndpoint = browser.wsEndpoint();
    browser.disconnect(); // Disconnect from the browser, but don't close it
    console.log(browserWSEndpoint); // Communicate the Websocket URL to the client-side
    // example output: ws://127.0.0.1:55620/devtools/browser/e62ec4c8-1f05-42a1-86ce-7b8dd3403f91
})();

现在,您可以通过客户端的操纵up捆绑包来控制客户端上运行的浏览器(在服务器上运行)。在这种情况下,您现在可以通过puppeteer.connect连接到浏览器并以这种方式制作屏幕截图。

我强烈建议您使用选项1,因为在选项2中,您正在将正在运行的浏览器完全暴露给客户端。即使使用选项1,您仍然需要处理用户输入验证,超时,导航错误等。



 类似资料:
  • 我尝试为我的网站提供文件上传/下载服务,并且尝试使用openstack中的对象存储。问题是,我通过php和openstack PHPSDK做这件事没有问题,但是当我试图通过一些javascript做这件事时,我找不到一个好的sdk或方法。我没有使用node,我有一个php服务器和一个javascript客户端。我想直接从javascript客户端上传或下载文件。我不希望文件通过php服务器传输。我

  • 我正在spring应用程序中使用web客户端 我在执行相同操作时面临内存泄漏问题 我正在使用下面的代码获取来自服务的非2xx响应的响应体: 我的问题是,如果我在responseMono上使用dispose方法,处理过程需要很长的时间,而没有它,我会面临内存泄漏问题。我在这里做错什么了吗?

  • 我有一个类,它使用了一个FIGEN客户端。以前,我使用Mockito,并为每个方法调用提供了一个存储的响应。现在我想使用WireMock,这样我就可以看到我的代码正确处理不同类型的响应代码。我该怎么做呢?我不知道如何在测试中连接我的Faignn客户端,并连接它,使它使用WiRemote ck而不是我在我的文件中设置的网址。任何建议都将不胜感激。

  • 创建okhttp3客户端,使用sni地址访问web服务器,但握手失败,错误消息为“javax.net.ssl.SSLHandShakeException:握手期间远程主机关闭连接”。 有人知道如何在okhttp3客户端代码中使用sni地址吗?

  • 1. 创建 Maven 工程 服务端部署完毕后,我们可以新建一个 Maven 工程使用 SOFARegistry 提供的服务。首先新建一个 Maven 工程,然后引入如下依赖: <dependency> <groupId>com.alipay.sofa</groupId> <artifactId>registry-client-all</artifactId> <versi

  • 发起请求 让我们从导入aiohttp模块开始: import aiohttp 好啦,我们来尝试获取一个web页面。比如我们来获取下GitHub的时间轴。 async with aiohttp.ClientSession() as session: async with session.get('https://api.github.com/events') as resp: