需求概述
我们希望通过 React 构建一个简单的电子签名组件,用户可以在画布上手写签名,完成后可以保存签名并将其上传到服务器。具体需求如下:
- 用户可以在画布上自由绘制签名。
- 提供“清除”按钮,允许用户重置签名。
- 提供“保存”按钮,将签名保存为图片文件并上传到服务器。
- 签名应以 Base64 格式保存,并转换为文件流后上传。
- 使用 MinIO 作为文件存储服务,确保签名文件的安全性和持久性。
技术栈选择
react-signature-canvas
:一个轻量级的 React 组件,允许用户在画布上绘制签名。minio.js
:用于与 MinIO 服务器交互,上传签名文件。BottomClock
:自定义组件,用于提供底部的操作按钮(重写和确认)。
安装依赖
安装所需的依赖库:
npm install react-signature-canvas antd-mobile @minio-js/minio
react-signature-canvas
:用于绘制签名。@minio-js/minio
:用于与 MinIO 服务器进行文件上传。
创建签名组件
引入 SignatureCanvas
react-signature-canvas
是一个非常方便的 React 组件,它提供了画布绘制的功能。我们可以通过 ref
来控制画布的行为,例如清除签名、获取签名数据等。
import React, { useRef } from "react";
import SignatureCanvas from "react-signature-canvas";const sigCanvas = useRef();// 清除签名
const clearSignature = () => {if (sigCanvas.current) {sigCanvas.current.clear();}
};// 获取签名的 Base64 数据
const getSignatureData = () => {if (sigCanvas.current) {return sigCanvas.current.toDataURL();}return null;
};
将 Base64 转换为文件流
签名数据是以 Base64 格式返回的,为了将其上传到服务器,我们需要将其转换为文件流。我们可以编写一个辅助函数 dataURLtoFile
来完成这个任务:
const dataURLtoFile = (base64, fileName) => {const data = base64.split(",");const type = data[0].match(/:(.*?);/)[1];const suffix = type.split("/")[1];const bstr = window.atob(data[1]);let n = bstr.length;const u8arr = new Uint8Array(n);while (n--) {u8arr[n] = bstr.charCodeAt(n);}return new File([u8arr], `${fileName}.${suffix}`, { type: type });
};
保存签名并上传
import { uploadFileFun } from "@/utils/minio.js";
import { Toast } from "antd-mobile";const saveSignature = async () => {const isEmpty = sigCanvas.current.isEmpty(); // 检查是否为空if (!isEmpty && sigCanvas.current) {const dataUrl = sigCanvas.current.toDataURL(); // 获取签名的 Base64 数据try {const file = dataURLtoFile(dataUrl, "signature");const response = await uploadFileFun(file); // 上传文件到 MinIOconsole.log("签名已成功上传:", response);props.saveSignature(response); // 将上传结果传递给父组件} catch (error) {console.error("上传失败:", error);Toast.show({content: "签名上传失败,请稍后再试。",});}} else {Toast.show({content: "请先完成签名。",});}
};
完整的签名组件代码
import React, { useRef } from "react";
import SignatureCanvas from "react-signature-canvas";
import BottomClock from "@/components/BottomClick/index";
import { Toast } from "antd-mobile";
import { uploadFileFun } from "@/utils/minio.js";export default function Signature(props) {const sigCanvas = useRef();// 清除签名const clearSignature = () => {if (sigCanvas.current) {sigCanvas.current.clear();}};// Base64格式转文件流const dataURLtoFile = (base64, fileName) => {const data = base64.split(",");const type = data[0].match(/:(.*?);/)[1];const suffix = type.split("/")[1];const bstr = window.atob(data[1]);let n = bstr.length;const u8arr = new Uint8Array(n);while (n--) {u8arr[n] = bstr.charCodeAt(n);}return new File([u8arr], `${fileName}.${suffix}`, { type: type });};// 保存签名const saveSignature = async () => {const isEmpty = sigCanvas.current.isEmpty(); // 检查是否为空if (!isEmpty && sigCanvas.current) {const dataUrl = sigCanvas.current.toDataURL(); // 获取签名的 Base64 数据try {const file = dataURLtoFile(dataUrl, "signature");const response = await uploadFileFun(file); // 上传文件到 MinIOconsole.log("签名已成功上传:", response);props.saveSignature(response); // 将上传结果传递给父组件} catch (error) {console.error("上传失败:", error);Toast.show({content: "签名上传失败,请稍后再试。",});}} else {Toast.show({content: "请先完成签名。",});}};return (<div className="signaturecontainer"><SignatureCanvaspenColor="#0000ff" // 设置画笔颜色为蓝色maxWidth={6}canvasProps={{className: "sigCanvas",}}ref={sigCanvas}/><div className="button-container"><BottomClockreset={"重写"}confirm={"确认"}onReset={clearSignature}onSubmit={saveSignature}/></div></div>);
}
文件上传到 MinIO
配置 MinIO
MinIO 是一个高性能的对象存储系统,适合用于存储文件。我们可以通过 minio.js
库与 MinIO 服务器进行交互。首先,确保你已经安装并配置了 MinIO 服务器,并且有一个可用的存储桶。
编写上传函数
在 @/utils/minio.js
中编写一个上传文件的函数 uploadFileFun
,该函数将接收文件对象并将其上传到 MinIO 服务器:
import { Client, S3Error } from "@minio-js/minio";const minioClient = new Client({endPoint: "your-minio-endpoint", // 替换为你的 MinIO 服务器地址port: 9000, // 替换为你的 MinIO 服务器端口useSSL: false, // 是否使用 HTTPSaccessKey: "your-access-key", // 替换为你的 MinIO 访问密钥secretKey: "your-secret-key", // 替换为你的 MinIO 秘钥
});export const uploadFileFun = async (file) => {try {const bucketName = "signatures"; // 存储桶名称const objectName = file.name; // 文件名const metaData = {"Content-Type": file.type,};// 上传文件到 MinIOawait minioClient.putObject(bucketName, objectName, file, metaData);// 获取文件的 URLconst url = await minioClient.presignedGetObject(bucketName, objectName, 60 * 60 * 24); // 签名链接有效期为 24 小时return url;} catch (err) {if (err instanceof S3Error) {console.error("MinIO 错误:", err);} else {console.error("未知错误:", err);}throw err;}
};
参考资料
- React Signature Canvas
- MinIO JavaScript SDK
- Ant Design Mobile