如何基于Socket.io与React扩展聊天应用支持PDF、视频等多类型文件传输?
实现Socket.io+React聊天应用的多类型文件发送与接收
当前代码仅支持图片传输,要扩展到PDF、视频等文件,核心是在传输时携带文件的MIME类型、文件名信息,客户端根据类型做差异化展示/处理,服务端只需转发完整的文件信息对象即可。
修改后的服务端代码
服务端无需复杂逻辑,仅需转发包含完整文件信息的对象:
// index.js const express = require('express'); const app = express(); const PORT = 8080; const http = require('http').createServer(app); const cors = require('cors'); const io = require('socket.io')(http, { cors: { origin: ["http://localhost:3001"] } }); io.on('connection', (socket) => { console.log(`⚡: ${socket.id} user just connected!`); // 接收完整文件信息对象并广播给所有客户端 socket.on('fileUpload', (fileData) => { io.emit('fileUploadResponse', fileData); }) }); http.listen(PORT, () => { console.log(`Server listening on ${PORT}`); });
修改后的客户端代码
重构状态管理以支持多文件消息,同时处理不同类型文件的读取与展示:
import React, { useEffect, useState } from 'react'; const ChatBody = ({ socket }) => { const [selectedFile, setSelectedFile] = useState(null); // 存储所有文件消息,每个消息包含文件名、MIME类型、Base64编码 const [fileMessages, setFileMessages] = useState([]); useEffect(() => { socket.on("fileUploadResponse", (fileData) => { // 将新收到的文件消息添加到列表 setFileMessages(prev => [...prev, fileData]); }) }, [socket]) // 读取文件为Base64格式(去掉data:xxx;base64,前缀) const readFileAsBase64 = (file) => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result.split(',')[1]); reader.onerror = reject; reader.readAsDataURL(file); }); }; const handleFileChange = (e) => { setSelectedFile(e.target.files[0]); }; const handleFileUpload = async () => { if (!selectedFile) return; const base64Data = await readFileAsBase64(selectedFile); // 构造包含完整信息的文件对象 const fileData = { name: selectedFile.name, type: selectedFile.type, base64: base64Data }; socket.emit('fileUpload', fileData); // 清空已选择的文件 setSelectedFile(null); }; // 根据文件类型渲染对应的展示组件 const renderFileMessage = (msg) => { const dataUrl = `data:${msg.type};base64,${msg.base64}`; if (msg.type.startsWith('image/')) { return <img style={{ width: '150px', height: 'auto', margin: '8px 0' }} src={dataUrl} alt={msg.name} />; } else if (msg.type === 'application/pdf') { return <embed style={{ width: '300px', height: '400px', margin: '8px 0' }} src={dataUrl} type="application/pdf" title={msg.name} />; } else if (msg.type.startsWith('video/')) { return <video style={{ width: '300px', margin: '8px 0' }} controls src={dataUrl} title={msg.name} />; } else { // 其他类型文件提供下载链接 return <a style={{ display: 'block', margin: '8px 0' }} href={dataUrl} download={msg.name} > 下载文件: {msg.name} </a>; } }; return ( <div style={{ padding: '16px' }}> {/* 展示历史文件消息 */} <div className="file-messages"> {fileMessages.map((msg, index) => ( <div key={index} className="file-message"> {renderFileMessage(msg)} </div> ))} </div> {/* 文件上传区域 */} <div style={{ marginTop: '16px' }}> <input type="file" onChange={handleFileChange} disabled={!!selectedFile} /> <button onClick={handleFileUpload} disabled={!selectedFile}>上传文件</button> </div> </div> ); }; export default ChatBody;
关键实现说明
- 文件信息完整性:客户端将文件名、MIME类型、Base64编码打包发送,确保服务端转发后客户端能准确识别文件类型
- 差异化渲染:通过
msg.type判断文件类型,分别用img、embed、video标签支持预览,非预览类型提供下载链接 - Base64优化:读取文件时去掉
data:xxx;base64,前缀,减少传输的数据量 - 多消息管理:用数组存储所有文件消息,支持连续发送多文件的展示
内容的提问来源于stack exchange,提问作者MOHAMMAD SIDDIQUE
相关产品推荐
相关产品推荐

