Nano Banana开发无限画布分享|免费调用gemini-2.5-flash-image-preview
我开发了一款nano banana无限画布的创意应用集成了gemini-2.5-flash-image-preview。前端采用React 19.1.1+TypeScript+Vite技术栈,后端使用Node.js+Express+MySQL。核心创新点包括:1)基于React 19的无限画布组件,支持平移/缩放/框选等交互;2)统一的可变换元素系统,所有元素共享拖拽/缩放/旋转逻辑
我开发了一款无限画布应用:
项目背景
大家好,我是一名前端开发者。最近我完成了一个让我非常兴奋的项目——nano banana无限画布应用。这是一个集成了AI图像生成、实时协作、项目管理的现代化创意平台。今天我想和大家分享一下这个项目的开发经历和技术实现。在开始开发之前,我花了很多时间思考技术栈的选择。作为一个前端开发者,我对React比较熟悉,所以前端选择了React 19.1.1。说实话,React 19的新特性让我很兴奋,特别是并发渲染和Suspense的改进。
前端技术栈
- React 19.1.1 - 我选择了最新版本的React,主要是想体验一下新特性
- TypeScript - 这个选择让我在开发过程中少了很多bug,类型检查真的很重要
- Vite - 相比Webpack,Vite的启动速度真的快很多,开发体验很棒
- Tailwind CSS - 原子化CSS让我不用写很多自定义样式,开发效率提升明显
- 自定义Hooks - 我把很多业务逻辑封装成了Hooks,代码复用性很好
后端技术栈
- Node.js + Express - 选择Node.js主要是为了前后端语言统一,减少学习成本
- MySQL 8.0+ - 关系型数据库,数据结构比较清晰
- JWT - 无状态的认证方式,适合分布式部署
- Socket.IO - 实现实时协作功能,用户体验很重要
- Multer + Sharp - 文件上传和图像处理,Sharp的性能真的很不错
- Google Gemini AI - AI功能是亮点,Gemini的API调用比较简单
前端架构:我的设计思路
1. 组件化设计:从混乱到清晰
刚开始开发的时候,我把所有逻辑都写在一个大组件里,结果代码越来越乱。后来我意识到需要重新设计架构,于是采用了高度模块化的组件设计。
核心画布组件 - InfiniteCanvas
这个组件是整个应用的核心,我花了很多时间优化它。让我分享一下我的实现思路:
// 无限画布的核心实现
export const InfiniteCanvas = forwardRef<CanvasApi, InfiniteCanvasProps>(({
elements,
selectedElementIds,
onSelectElement,
onMarqueeSelect,
onUpdateElement,
onInteractionEnd,
setResetViewCallback,
onGenerate,
onContextMenu,
onEditDrawing,
imageStyle,
onSetImageStyle,
}, ref) => {
// 画布状态管理
const [pan, setPan] = useState<Point>({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const [isPanning, setIsPanning] = useState(false);
const [marqueeRect, setMarqueeRect] = useState<MarqueeRect | null>(null);
设计亮点:
- 使用
forwardRef
暴露画布API给父组件,这样父组件可以直接控制画布 - 实现了完整的2D变换系统,支持平移、缩放、旋转,用户体验很流畅
- 支持框选和多重选择,这个功能让我调试了很久
- 集成了AI生成功能,用户可以直接在画布上生成图像
可变换元素组件 - TransformableElement
这个组件是我最得意的设计之一。我设计了一个统一的元素交互系统,支持拖拽、缩放、旋转等操作:
// 支持拖拽、缩放、旋转的通用元素组件
export const TransformableElement: React.FC<TransformableElementProps> = ({
element, isSelected, zoom, onSelect, onUpdate, onInteractionEnd, onContextMenu, onEditDrawing
}) => {
const [interaction, setInteraction] = useState<Interaction>(null);
const [isEditing, setIsEditing] = useState(false);
我的实现思路:
- 统一的元素交互系统,所有元素都使用相同的交互逻辑
- 支持拖拽、缩放、旋转操作,用户操作很直观
- 实时预览和状态同步,不会有延迟感
- 双击编辑功能,用户体验很好
说实话,这个组件的交互逻辑让我调试了很久,特别是旋转和缩放的数学计算,我花了很多时间才搞明白。
2. 状态管理:我的Hooks设计哲学
在状态管理方面,我没有使用Redux或者Zustand这些状态管理库,而是选择了自定义Hooks的方式。我觉得这样更灵活,也更符合React的设计理念。
我的自定义Hooks设计
useAuth Hook - 认证状态管理
这个Hook让我很满意,它处理了所有的认证逻辑:
export const useAuth = () => {
const [authState, setAuthState] = useState<AuthState>({
user: null,
isAuthenticated: false,
isLoading: true,
error: null
});
// 自动检查认证状态
const checkAuth = useCallback(async () => {
const token = localStorage.getItem('auth_token');
if (!token) {
setAuthState(prev => ({ ...prev, isLoading: false }));
return;
}
// JWT验证逻辑...
}, []);
useProject Hook - 项目管理
这个Hook管理项目的所有状态,包括元素、保存状态等:
export const useProject = () => {
const [projectState, setProjectState] = useState<ProjectState>({
currentProject: null,
elements: [],
isLoading: false,
error: null,
hasUnsavedChanges: false
});
// 防抖保存机制
const saveTimeoutRef = useRef<NodeJS.Timeout | null>(null);
我实现了一个防抖保存机制,用户操作后2秒自动保存,这样既不会频繁调用API,也不会丢失用户的数据。
useHistoryState Hook - 历史记录管理
这个Hook是我最得意的设计之一,它实现了类似Photoshop的历史记录功能:
export const useHistoryState = <T>(initialState: T) => {
const [history, setHistory] = useState<T[]>([initialState]);
const [currentIndex, setCurrentIndex] = useState(0);
const setState = useCallback((
action: T | ((prevState: T) => T),
options: SetStateOptions = { addToHistory: true }
) => {
// 智能历史记录管理
}, [history, currentIndex]);
用户可以通过Ctrl+Z撤销操作,Ctrl+Y重做操作,体验很好。
3. TypeScript:我的类型安全之路
刚开始开发的时候,我使用的是JavaScript,结果遇到了很多类型错误。后来我决定迁移到TypeScript,这个决定让我少了很多bug。
我的类型系统设计
我设计了一个严格的类型系统,特别是元素类型:
// 基础类型定义
export interface Point {
x: number;
y: number;
}
export type ElementType = 'note' | 'image' | 'arrow' | 'drawing';
// 元素类型系统
interface BaseElement {
id: string;
position: Point;
width: number;
height: number;
rotation: number;
zIndex: number;
}
export interface NoteElement extends BaseElement {
type: 'note';
content: string;
color: string;
textAlign?: 'left' | 'center' | 'right';
}
export interface ImageElement extends BaseElement {
type: 'image';
src: string;
}
export interface ArrowElement extends BaseElement {
type: 'arrow';
start: Point;
end: Point;
color: string;
}
export interface DrawingElement extends BaseElement {
type: 'drawing';
src: string; // base64 data URL
}
export type CanvasElement = NoteElement | ImageElement | ArrowElement | DrawingElement;
这个类型系统让我在开发过程中能够提前发现很多潜在的问题,特别是联合类型的使用,让代码更加安全。
后端开发:从零开始的Node.js之旅
作为一个前端开发者,后端开发对我来说是一个挑战。我选择了Node.js + Express,主要是为了保持技术栈的一致性。
1. RESTful API:我的设计思路
我采用了标准的RESTful API设计模式,这样前端调用起来比较直观。
认证系统:安全第一
用户认证是我最关注的部分,我实现了完整的JWT认证系统:
// 用户注册
router.post('/register', validateUserRegistration, async (req, res) => {
try {
const { username, email, password, displayName } = req.body;
// 密码哈希处理
const saltRounds = 12;
const passwordHash = await bcrypt.hash(password, saltRounds);
// JWT令牌生成
const token = jwt.sign(
{ userId: userId },
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRES_IN || '7d' }
);
// 会话管理
const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
await executeQuery(
'INSERT INTO user_sessions (user_id, token_hash, expires_at, ip_address, user_agent) VALUES (?, ?, ?, ?, ?)',
[userId, tokenHash, expiresAt, req.ip, req.get('User-Agent')]
);
} catch (error) {
// 错误处理
}
});
我使用了bcrypt来哈希密码,JWT来管理会话,还记录了用户的IP和User-Agent,这样安全性更高。
画布项目管理:数据一致性很重要
项目管理是我后端开发的重点,我使用了事务来保证数据一致性:
// 创建项目
router.post('/', authenticateToken, validateProjectCreation, logActivity('create_project'), async (req, res) => {
try {
const { title, description, isPublic, elements } = req.body;
const projectId = require('crypto').randomUUID();
// 事务处理
const queries = [
{
query: 'INSERT INTO canvas_projects (id, user_id, title, description, is_public) VALUES (?, ?, ?, ?, ?)',
params: [projectId, req.user.id, title, description, isPublic || false]
}
];
await executeTransaction(queries);
} catch (error) {
// 错误处理
}
});
我使用了UUID作为项目ID,这样在分布式环境下也不会有冲突。事务处理让我不用担心数据不一致的问题。
2. 数据库设计:我的MySQL学习之路
数据库设计对我来说是一个新的领域,我选择了MySQL,主要是因为它比较成熟,文档也比较完善。
我的表结构设计
我设计了几个核心表来存储应用数据:
-- 用户表
CREATE TABLE IF NOT EXISTS users (
id VARCHAR(36) PRIMARY KEY DEFAULT (UUID()),
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
display_name VARCHAR(100),
avatar_url VARCHAR(500),
is_active BOOLEAN DEFAULT TRUE,
last_login_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 画布项目表
CREATE TABLE IF NOT EXISTS canvas_projects (
id VARCHAR(36) PRIMARY KEY DEFAULT (UUID()),
user_id VARCHAR(36) NOT NULL,
title VARCHAR(200) NOT NULL DEFAULT 'Untitled Canvas',
description TEXT,
thumbnail_url VARCHAR(500),
is_public BOOLEAN DEFAULT FALSE,
is_template BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- 画布元素表
CREATE TABLE IF NOT EXISTS canvas_elements (
id VARCHAR(36) PRIMARY KEY DEFAULT (UUID()),
project_id VARCHAR(36) NOT NULL,
element_type ENUM('note', 'image', 'arrow', 'drawing') NOT NULL,
position_x DECIMAL(10, 2) NOT NULL,
position_y DECIMAL(10, 2) NOT NULL,
width DECIMAL(10, 2) NOT NULL,
height DECIMAL(10, 2) NOT NULL,
rotation DECIMAL(5, 2) DEFAULT 0,
z_index INT DEFAULT 0,
element_data JSON NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (project_id) REFERENCES canvas_projects(id) ON DELETE CASCADE
);
我的设计思路:
- 使用UUID作为主键,这样在分布式环境下也不会有冲突
- JSON字段存储元素数据,这样我可以灵活地存储不同类型的元素
- 完善的索引设计,查询性能更好
- 外键约束保证数据一致性,不会出现孤儿数据
3. AI服务集成:让应用更智能
AI功能是这个应用的亮点之一,我集成了Google Gemini AI来实现图像生成和编辑功能。
我的AI服务设计
我创建了一个AIService类来封装所有的AI相关功能:
class AIService {
constructor() {
this.apiKey = process.env.AI_API_KEY;
this.baseUrl = process.env.AI_API_URL || 'https://ai.juguang.chat/v1beta/models/gemini-2.5-flash-image-preview:generateContent';
}
async generateTextImage(prompt) {
try {
const response = await fetch(this.baseUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
},
body: JSON.stringify({
contents: [
{
parts: [
{
text: prompt
}
]
}
]
})
});
const data = await response.json();
// 提取生成的图像数据
for (const candidate of data.candidates) {
for (const part of candidate.content.parts) {
if (part.inlineData) {
return `data:${part.inlineData.mimeType};base64,${part.inlineData.data}`;
}
}
}
return null;
} catch (error) {
console.error('Generate text image failed:', error);
throw error;
}
}
}
我的AI功能实现:
- 文本到图像生成:用户输入描述,AI生成对应的图像
- 图像编辑和对象移除:用户可以编辑现有图像
- 多种艺术风格支持:包括像素风、油画风、水彩风等
- 自动项目创建:AI生成图像时自动创建项目
说实话,AI功能的集成让我学到了很多,特别是如何处理异步请求和错误处理。
4. 文件处理:让用户上传更安全
文件上传是一个重要的功能,我需要确保用户上传的文件是安全的,并且处理效率要高。
我的文件处理方案
我使用了Multer来处理文件上传,Sharp来处理图像:
// Multer配置
const storage = multer.diskStorage({
destination: async (req, file, cb) => {
const uploadDir = process.env.UPLOAD_DIR || 'uploads';
try {
await fs.mkdir(uploadDir, { recursive: true });
cb(null, uploadDir);
} catch (error) {
cb(error);
}
},
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
const ext = path.extname(file.originalname);
cb(null, `file-${uniqueSuffix}${ext}`);
}
});
// 图像处理
if (file.mimetype.startsWith('image/')) {
try {
const processedPath = file.path.replace(path.extname(file.path), '_processed' + path.extname(file.path));
await sharp(file.path)
.resize(2048, 2048, {
fit: 'inside',
withoutEnlargement: true
})
.jpeg({ quality: 85 })
.toFile(processedPath);
await fs.unlink(file.path);
await fs.rename(processedPath, file.path);
} catch (error) {
console.error('Image processing error:', error);
}
}
我实现了文件类型验证、大小限制、图像压缩等功能,这样既保证了安全性,又优化了存储空间。
核心功能:我的技术实现之路
1. 无限画布:最复杂的部分
无限画布是这个应用的核心功能,也是我花费最多时间开发的部分。我需要实现一个流畅的2D变换系统。
坐标系统:数学是基础
我设计了一个双坐标系统,屏幕坐标和世界坐标之间的转换:
// 屏幕坐标到世界坐标的转换
const screenToWorld = useCallback((screenPoint: Point): Point => {
return {
x: (screenPoint.x - pan.x) / zoom,
y: (screenPoint.y - pan.y) / zoom,
};
}, [pan, zoom]);
// 世界坐标到屏幕坐标的转换
const worldToScreen = useCallback((worldPoint: Point): Point => {
return {
x: worldPoint.x * zoom + pan.x,
y: worldPoint.y * zoom + pan.y,
};
}, [pan, zoom]);
这个坐标转换让我调试了很久,特别是缩放时的坐标计算。
变换系统:用户体验的关键
元素变换是这个功能的核心,我实现了拖拽、缩放、旋转等操作:
// 元素变换处理
const handleInteractionMove = useCallback((e: MouseEvent) => {
if (!interaction) return;
const { type, startPoint, startElement } = interaction;
const dx = (e.clientX - startPoint.x) / zoom;
const dy = (e.clientY - startPoint.y) / zoom;
if (type === 'drag') {
const newPosition = { x: startElement.position.x + dx, y: startElement.position.y + dy };
onUpdate({ ...startElement, position: newPosition });
} else if (type === 'resize') {
const rad = startElement.rotation * (Math.PI / 180);
const cos = Math.cos(-rad);
const sin = Math.sin(-rad);
const rotDx = dx * cos - dy * sin;
const rotDy = dx * sin + dy * cos;
const newWidth = Math.max(20, startElement.width + rotDx);
const newHeight = Math.max(20, startElement.height + rotDy);
onUpdate({
...startElement,
width: newWidth,
height: newHeight
});
}
}, [interaction, onUpdate, zoom]);
旋转和缩放的数学计算让我重新学习了三角函数,不过最终的效果让我很满意。
2. 实时协作:让多人同时创作
实时协作功能让多个用户可以同时编辑同一个项目,这个功能让我学到了很多关于WebSocket的知识。
Socket.IO:我的实时通信方案
我选择了Socket.IO来实现实时通信,因为它提供了很好的错误处理和重连机制:
// Socket.IO服务器配置
const io = new Server(server, {
cors: {
origin: "*",
methods: ["GET", "POST"],
credentials: true
}
});
// Socket事件处理
setupSocketHandlers(io);
// 客户端连接处理
io.on('connection', (socket) => {
console.log('User connected:', socket.id);
socket.on('join-project', (projectId) => {
socket.join(projectId);
console.log(`User ${socket.id} joined project ${projectId}`);
});
socket.on('element-update', (data) => {
socket.to(data.projectId).emit('element-updated', data);
});
});
这个功能让我体验到了真正的实时协作,用户可以看到其他用户的实时操作,就像Google Docs一样。
3. 绘图系统:让创意自由表达
绘图功能让用户可以在画布上自由创作,我实现了一个功能完整的绘图工具。
Canvas绘图:我的实现方案
我使用HTML5 Canvas来实现绘图功能,并添加了历史记录管理:
// 绘图模态框组件
export const DrawingModal: React.FC<DrawingModalProps> = ({ element, onSave, onClose }) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const contextRef = useRef<CanvasRenderingContext2D | null>(null);
const [isDrawing, setIsDrawing] = useState(false);
const [tool, setTool] = useState<'pencil' | 'eraser'>('pencil');
const [color, setColor] = useState('#000000');
const [brushSize, setBrushSize] = useState(5);
// 历史记录管理
const [history, setHistory] = useState<ImageData[]>([]);
const [historyIndex, setHistoryIndex] = useState(-1);
const saveHistoryState = useCallback(() => {
const context = contextRef.current;
if (!context) return;
const imageData = context.getImageData(0, 0, context.canvas.width, context.canvas.height);
setHistory(prev => {
const newHistory = prev.slice(0, historyIndex + 1);
return [...newHistory, imageData];
});
setHistoryIndex(prev => prev + 1);
}, [historyIndex]);
// 绘图事件处理
const startDrawing = useCallback((e: React.MouseEvent) => {
const point = getCanvasPoint(e);
const context = contextRef.current;
if (!point || !context) return;
context.strokeStyle = color;
context.lineWidth = brushSize;
context.globalCompositeOperation = tool === 'pencil' ? 'source-over' : 'destination-out';
context.beginPath();
context.moveTo(point.x, point.y);
setIsDrawing(true);
}, [tool, color, brushSize, getCanvasPoint]);
};
我实现了铅笔、橡皮擦、多种颜色、不同画笔大小等功能,还支持撤销和重做,用户体验很好。
4. 图像编辑:AI让创意更智能
图像编辑功能是这个应用的亮点之一,我集成了AI来实现智能图像编辑。
AI图像编辑:我的实现思路
我实现了一个完整的图像编辑系统,用户可以编辑现有图像或移除不需要的对象:
// 图像编辑模态框
export const ImageEditModal: React.FC<ImageEditModalProps> = ({ element, onSave, onClose }) => {
const [tool, setTool] = useState<'brush' | 'eraser'>('brush');
const [brushSize, setBrushSize] = useState(20);
const [prompt, setPrompt] = useState('');
const [isLoading, setIsLoading] = useState(false);
// 蒙版合成
const compositeImageWithMask = useCallback(async (baseSrc: string, maskDataUrl: string): Promise<string> => {
const image = new Image();
image.crossOrigin = "anonymous";
image.src = baseSrc;
await new Promise((resolve, reject) => {
image.onload = resolve;
image.onerror = reject;
});
const maskImage = new Image();
maskImage.src = maskDataUrl;
await new Promise((resolve, reject) => {
maskImage.onload = resolve;
maskImage.onerror = reject;
});
const compositeCanvas = document.createElement('canvas');
compositeCanvas.width = image.naturalWidth;
compositeCanvas.height = image.naturalHeight;
const ctx = compositeCanvas.getContext('2d');
if (!ctx) throw new Error("Could not create canvas context");
ctx.drawImage(image, 0, 0);
ctx.drawImage(maskImage, 0, 0);
return compositeCanvas.toDataURL('image/png');
}, []);
// AI生成处理
const runGeneration = async (context: GenerationContext) => {
setIsLoading(true);
try {
const compositeImageBase64 = await compositeImageWithMask(context.baseImageSrc, context.maskDataUrl);
const [header, data] = compositeImageBase64.split(',');
const mimeType = header.match(/data:(.*);base64/)?.[1] || 'image/png';
let textPrompt = '';
if (context.type === 'remove') {
textPrompt = '仔细移除半透明红色高亮的对象或区域,并真实地填充背景(修复)。';
} else {
textPrompt = `在半透明红色高亮的区域中,${context.prompt}。使其看起来自然且无缝。`;
}
// 调用后端API
const response = await fetch('/api/ai/generate-image-with-image', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
prompt: textPrompt,
image: compositeImageBase64,
mimeType: mimeType
})
});
const result = await response.json();
if (result.success && result.imageData) {
setPreviewImageSrc(result.imageData);
}
} catch (error) {
console.error("Error editing image:", error);
} finally {
setIsLoading(false);
}
};
};
这个功能让我体验到了AI的强大,用户只需要简单描述想要的效果,AI就能生成相应的图像。
性能优化:让应用更流畅
在开发过程中,我发现性能优化是一个持续的过程。我采用了多种策略来提升应用的性能。
1. 前端性能优化:用户体验的关键
虚拟化渲染:只渲染可见元素
当画布上有大量元素时,渲染所有元素会导致性能问题。我实现了虚拟化渲染:
// 只渲染视口内的元素
const visibleElements = useMemo(() => {
return elements.filter(element => {
const elementScreenPos = worldToScreen(element.position);
return (
elementScreenPos.x + element.width > 0 &&
elementScreenPos.x - element.width < window.innerWidth &&
elementScreenPos.y + element.height > 0 &&
elementScreenPos.y - element.height < window.innerHeight
);
});
}, [elements, worldToScreen]);
这样只渲染用户能看到的元素,大大提升了性能。
防抖和节流:减少不必要的操作
我实现了防抖保存机制,避免频繁的API调用:
// 防抖保存
const saveTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const updateElements = useCallback((elements: CanvasElement[]) => {
setProjectState(prev => ({
...prev,
elements,
hasUnsavedChanges: true
}));
// 防抖保存,避免频繁API调用
if (projectState.currentProject) {
if (saveTimeoutRef.current) {
clearTimeout(saveTimeoutRef.current);
}
saveTimeoutRef.current = setTimeout(() => {
saveChanges();
}, 2000); // 2秒后自动保存
}
}, [projectState.currentProject, saveChanges]);
这样用户操作后2秒自动保存,既不会丢失数据,也不会频繁调用API。
2. 后端性能优化:数据库是关键
数据库连接池:提升并发性能
我使用了连接池来管理数据库连接,这样可以提高并发性能:
const createPool = () => {
if (!pool) {
pool = mysql.createPool({
host: process.env.DB_HOST || '43.139.101.176',
port: process.env.DB_PORT || 3306,
user: process.env.DB_USER || 'ban',
password: process.env.DB_PASSWORD || 'BBiBwsJcjCCaCHft',
database: process.env.DB_NAME || 'ban',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
charset: 'utf8mb4',
acquireTimeout: 60000,
ssl: false,
supportBigNumbers: true,
bigNumberStrings: true,
dateStrings: true
});
}
return pool;
};
连接池让我不用担心数据库连接的问题,系统会自动管理连接的生命周期。
查询优化:索引很重要
我设计了复合索引来优化查询性能:
-- 复合索引优化
CREATE INDEX idx_canvas_elements_composite ON canvas_elements(project_id, element_type, z_index);
CREATE INDEX idx_file_uploads_composite ON file_uploads(user_id, project_id, created_at);
CREATE INDEX idx_activity_logs_composite ON activity_logs(user_id, project_id, created_at);
这些索引大大提升了查询速度,特别是在处理大量数据时。
安全机制:保护用户数据
安全是我最关注的问题之一,我实现了多层安全机制来保护用户数据。
1. 身份认证:JWT + 会话管理
我使用了JWT令牌认证,配合会话管理:
- JWT令牌认证:无状态的认证方式
- 会话管理:记录用户的登录状态
- 密码哈希(bcrypt):安全的密码存储
- 令牌过期机制:定期更新令牌
2. 数据验证:防止恶意输入
我实现了严格的数据验证:
// 输入验证中间件
const validateProjectCreation = [
body('title').trim().isLength({ min: 1, max: 200 }).withMessage('标题长度必须在1-200字符之间'),
body('description').optional().trim().isLength({ max: 1000 }).withMessage('描述长度不能超过1000字符'),
body('isPublic').optional().isBoolean().withMessage('isPublic必须是布尔值'),
body('elements').optional().isArray().withMessage('elements必须是数组')
];
这样可以防止恶意输入和SQL注入攻击。
3. 文件安全:保护服务器
文件上传是一个安全风险点,我实现了多重保护:
- 文件类型验证:只允许特定类型的文件
- 文件大小限制:防止大文件攻击
- 安全的文件存储路径:避免路径遍历攻击
- 病毒扫描(可扩展):未来可以集成病毒扫描功能
未来规划:继续完善应用
虽然nano banana无限画布应用已经基本完成,但我还有很多想法想要实现。
结语:我的开发感悟
开发nano banana无限画布应用是一次非常有意义的经历。从最初的想法到最终的产品,我学到了很多技术知识,也积累了不少开发经验。
开发过程中的挑战
开发过程中遇到了很多挑战:
- 无限画布的坐标系统设计
- 元素变换的数学计算
- AI服务的集成和错误处理
- 性能优化和安全防护
对未来的展望
虽然这个应用还有很多可以改进的地方,但我对它的未来充满期待。我希望能够继续完善它,让它成为一个真正有用的创意工具。
如果你对这个项目感兴趣,或者有什么建议,欢迎和我交流。技术分享是程序员成长的重要方式,我也希望通过这篇文章能够帮助到其他开发者。
这就是我开发nano banana无限画布应用的故事。虽然名字有点奇怪,但这个项目让我学到了很多,也让我对Web开发有了更深的热爱。希望我的分享能够对大家有所帮助!
更多推荐
所有评论(0)