实现 React 电子签名功能:从零开始构建一个完整的解决方案
需求概述
我们希望通过 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); // 上传文件到 MinIO
console.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); // 上传文件到 MinIO
console.log("签名已成功上传:", response);
props.saveSignature(response); // 将上传结果传递给父组件
} catch (error) {
console.error("上传失败:", error);
Toast.show({
content: "签名上传失败,请稍后再试。",
});
}
} else {
Toast.show({
content: "请先完成签名。",
});
}
};
return (
<div className="signaturecontainer">
<SignatureCanvas
penColor="#0000ff" // 设置画笔颜色为蓝色
maxWidth={6}
canvasProps={{
className: "sigCanvas",
}}
ref={sigCanvas}
/>
<div className="button-container">
<BottomClock
reset={"重写"}
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, // 是否使用 HTTPS
accessKey: "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,
};
// 上传文件到 MinIO
await minioClient.putObject(bucketName, objectName, file, metaData);
// 获取文件的 URL
const 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