React中用html2canvas截图后无法更新后端Page的screenshot字段
问题:截图上传后端后Page数据未更新screenshot字段
我在React项目中用html2canvas对<TemplateRealEstate/>组件内的<FormList/>(展示房屋信息的列表组件)截图,因需在非父子关系组件中展示,Context方案未生效,故打算将截图上传至后端存储后再展示。页面路由为http://localhost:3000/pages/:pageId,创建Page时screenshot字段初始值为undefined。创建Page和模板后,点击按钮触发截图操作,前端Axios请求返回状态码201,但后端返回的Page数据中仍未添加或更新screenshot字段,尝试多种Content-Type均无效。
相关代码
前端TemplateRealEstate.js
import React, { useEffect, useState } from "react"; import { useParams } from "react-router-dom"; import axios from "axios"; import domtoimage from "dom-to-image"; import html2canvas from "html2canvas"; import FormList from "../components/FormList"; import "./TemplateRealEstate.css"; const TemplateRealEstate = () => { const [loadedTemplates, setLoadedTemplates] = useState([]); const [screenshot, setScreenshot] = useState(null); const [screenshotFile, setScreenshotFile] = useState(null); const [screenshotUrl, setScreenshotUrl] = useState(null); const pageId = useParams().pageId; // 将data URI转为Blob对象 const dataURItoBlob = (dataURI) => { const binaryString = window.atob(dataURI.split(",")[1]); const mimeType = dataURI.split(",")[0].split(":")[1].split(";"[0]); const arrayBuffer = new ArrayBuffer(binaryString.length); const uint8Array = new Uint8Array(arrayBuffer); for (let i = 0; i < binaryString.length; i++) { uint8Array[i] = binaryString.charCodeAt(i); } return new Blob([arrayBuffer], { type: mimeType }); }; const handleScreenshot = () => { const element = document.getElementById("my-element"); html2canvas(element) .then((canvas) => { const dataURI = canvas.toDataURL(); const blob = dataURItoBlob(dataURI); setScreenshotFile(blob); const formData = new FormData(); formData.append("screenshot", blob, "screenshot.png"); // 发送截图到后端 axios .patch(`http://localhost:8080/api/pages/${pageId}`, formData, { headers: { "Content-Type": "application/x-www-form-urlencoded", }, }) .then((res) => { console.log("Successfully sent screenshot to server:", res); setScreenshot(formData); }) .catch((err) => { console.error("Failed to send screenshot to server:", err); }); }) .catch((error) => { console.error("Failed to take screenshot:", error); }); }; useEffect(() => { if (screenshotFile) { const reader = new FileReader(); reader.onload = (event) => { setScreenshotUrl(event.target.result); }; reader.readAsDataURL(screenshotFile); } }, [screenshotFile]); return ( <div> <div id="my-element"> <FormList items={loadedTemplates} /> </div> <button onClick={redirectToForm}>Add house</button> <button type="button" onClick={handleScreenshot}> Set screenshot </button> <button onClick={handleDelete}>Delete Page</button> </div> ); }; export default TemplateRealEstate;
后端Page控制器
// PATCH http://localhost:8080/api/pages/:pid const updatePageById = async (req, res, next) => { const pageId = req.params.pid; const screenshot = req.file; try { page = await Page.findById(pageId); } catch (err) { const error = new HttpError( "Something went wrong, could not update a page with that ID.", 500 ); return next(error); } if (screenshot) page.screenshot = screenshot; try { await page.save(); } catch (err) { const error = new HttpError( "Something went wrong, could not update page", 500 ); return next(error); } res.status(201).json({ page: page.toObject({ getters: true }) }); };
解决方案
1. 修复前端请求头
用FormData上传文件时,Axios会自动生成包含boundary的正确Content-Type,手动设置application/x-www-form-urlencoded会导致后端无法解析文件。删除请求头配置:
axios.patch(`http://localhost:8080/api/pages/${pageId}`, formData) .then(res => { console.log("Successfully sent screenshot to server:", res); setScreenshot(res.data.page.screenshot); // 存储后端返回的截图路径 }) .catch(err => { console.error("Failed to send screenshot to server:", err); });
2. 后端配置文件解析中间件
处理FormData文件需借助multer中间件(以Express为例):
- 安装依赖:
npm install multer - 配置路由和存储规则:
const multer = require('multer'); // 配置文件存储路径和命名规则 const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, 'uploads/'); // 确保uploads文件夹已创建 }, filename: (req, file, cb) => { cb(null, Date.now() + '-' + file.originalname); // 避免文件名重复 } }); const upload = multer({ storage: storage }); // 在路由中添加中间件,指定接收名为screenshot的单文件 router.patch('/api/pages/:pid', upload.single('screenshot'), updatePageById);
3. 后端存储文件路径而非完整file对象
直接存储req.file会导致数据库无法序列化对象,应存储文件的访问路径:
const updatePageById = async (req, res, next) => { const pageId = req.params.pid; // 生成可访问的文件路径,根据后端部署情况调整 const screenshotPath = req.file ? `/uploads/${req.file.filename}` : null; let page; try { page = await Page.findById(pageId); } catch (err) { const error = new HttpError( 'Something went wrong, could not update a page with that ID.', 500 ); return next(error); } if (screenshotPath) { page.screenshot = screenshotPath; // 存储路径 } try { await page.save(); } catch (err) { const error = new HttpError( 'Something went wrong, could not update page', 500 ); return next(error); } res.status(200).json({ page: page.toObject({ getters: true }) }); // PATCH请求成功应返回200,201用于创建资源 };
4. 检查Page模型定义
确保MongoDB的Page模型中包含screenshot字段,类型为字符串:
const mongoose = require('mongoose'); const pageSchema = new mongoose.Schema({ // 其他字段... screenshot: { type: String, default: undefined } }); module.exports = mongoose.model('Page', pageSchema);
内容的提问来源于stack exchange,提问作者yuroni
相关产品推荐
相关产品推荐

