当前位置: 首页 > news >正文

从零实现JSON与图片文件上传功能

        在前端开发中,文件上传是一个常见但容易出错的功能点。本文将带你从零开始,一步步实现一个支持JSON和图片文件上传、预览的完整案例,帮助你深入理解文件处理的核心机制。


一、功能概述与技术选型

我们要实现的文件上传功能包含以下核心特性:

  • 支持拖拽上传和点击上传两种方式
  • 限制仅允许上传JSON文件和图片文件
  • 实现文件大小限制(最大5MB)
  • 提供图片预览功能
  • 提供JSON内容解析与格式化显示功能
  • 支持文件移除操作

技术选型

  • React + TypeScript 作为基础框架
  • Ant Design 组件库提供UI支持,特别是 Upload.Dragger 组件
  • FileReader API 处理文件读取操作

二、项目初始化与基础结构搭建

1. 创建基础组件

首先,我们创建一个基础的FileUploadDemo组件,并引入所需要的依赖

import React, { useState } from 'react';
import { Upload, message } from 'antd';
import type { UploadProps } from 'antd';
import { InboxOutlined } from '@ant-design/icons';const { Dragger } = Upload;const FileUploadDemo: React.FC = () => {// 后续功能将在这里实现return (<div style={{ maxWidth: 600, margin: '0 auto', padding: 20 }}><h2>文件上传示例</h2>{/* 后续将添加上传组件 */}</div>);
};export default FileUploadDemo;

三、核心状态管理设计

在组件中,我们需要管理以下状态:

// 文件列表状态 - 用于Upload组件显示
const [fileList, setFileList] = useState<any[]>([]);
// 当前选中的单个文件
const [selectedFile, setSelectedFile] = useState<File | null>(null);
// 图片预览相关状态
const [previewImage, setPreviewImage] = useState<string>('');
const [previewOpen, setPreviewOpen] = useState<boolean>(false);
// JSON预览相关状态
const [jsonContent, setJsonContent] = useState<any>(null);
const [jsonModalOpen, setJsonModalOpen] = useState<boolean>(false);

        这里有个小技巧:虽然我们限制一次只上传一个文件,但 Ant Design 的 Upload 组件要求 fileList 是数组格式,所以我们需要将单个文件转换为数组 [selectedFile] 提供给组件。


四、文件验证与选择功能实现

1. 文件类型与大小验证

我们首先实现 beforeUpload 函数,用于在文件上传前验证文件类型和大小:

const beforeUpload = (file: File) => {// 验证文件类型const isJSON = file.type === 'application/json' || file.name.endsWith('.json');const isImage = file.type.startsWith('image/');const isAllowedType = isJSON || isImage;if (!isAllowedType) {message.error('只能上传JSON文件或图片文件!');return Upload.LIST_IGNORE;}// 验证文件大小 (5MB)const isLessThan5M = file.size / 1024 / 1024 < 5;if (!isLessThan5M) {message.error('文件大小不能超过5MB!');return Upload.LIST_IGNORE;}// 处理选中的文件handleFile(file);// 返回false阻止自动上传return false;
};

技术点说明

  • file.type.startsWith('image/') 用于判断文件是否为图片类型
  • 对于JSON文件,我们同时检查 file.type 和文件扩展名 .json 以提高兼容性
  • Upload.LIST_IGNORE 用于在验证失败时忽略当前文件
  • 最后返回 false 以阻止组件的自动上传行为,因为我们要实现自定义处理逻辑

2. 文件选择处理函数

接下来实现 handleFile 函数,用于处理用户选择的文件:

const handleFile = (file: File) => {setSelectedFile(file);setFileList([file]);// 根据文件类型分别处理if (file.type.startsWith('image/')) {// 图片文件处理逻辑将在后续实现} else if (file.type === 'application/json' || file.name.endsWith('.json')) {// JSON文件处理逻辑将在后续实现}
};

五、文件读取与预览功能实现

1. 图片文件预览实现

图片预览需要使用 FileReader API 的 readAsDataURL 方法,这是一个异步操作

const handlePreview = () => {if (selectedFile && selectedFile.type.startsWith('image/')) {setPreviewOpen(true);} else if (selectedFile && (selectedFile.type === 'application/json' || selectedFile.name.endsWith('.json'))) {setJsonModalOpen(true);}
};// 更新handleFile函数,添加图片处理逻辑
const handleFile = (file: File) => {setSelectedFile(file);setFileList([file]);// 根据文件类型分别处理if (file.type.startsWith('image/')) {// 创建FileReader实例const reader = new FileReader();// 设置onload事件处理函数 - 当文件读取完成后触发reader.onload = (e) => {// 将读取到的base64字符串设置到预览图片状态中setPreviewImage(e.target?.result as string);};// 以DataURL形式读取文件 - 这是一个异步操作reader.readAsDataURL(file);} else if (file.type === 'application/json' || file.name.endsWith('.json')) {// JSON文件处理逻辑将在后续实现}
};

异步操作说明

  • FileReader.readAsDataURL() 是一个异步方法,不会立即返回结果
  • 我们需要通过监听 onload 事件来获取读取完成后的结果
  • 读取完成后,e.target?.result 将包含图片的 Base64 编码字符

2. JSON文件内容解析实现

对于JSON文件,我们使用 FileReader.readAsText 方法读取文件内容,然后使用 JSON.parse 解析:

// 更新handleFile函数,添加JSON处理逻辑
const handleFile = (file: File) => {setSelectedFile(file);setFileList([file]);if (file.type.startsWith('image/')) {// 图片处理逻辑...} else if (file.type === 'application/json' || file.name.endsWith('.json')) {const reader = new FileReader();reader.onload = (e) => {try {// 先将文件内容转换为字符串const jsonText = e.target?.result as string;// 然后将JSON字符串解析为JavaScript对象const parsedJson = JSON.parse(jsonText);setJsonContent(parsedJson);} catch (error) {message.error('JSON文件解析错误');console.error('JSON解析错误:', error);}};// 以文本形式读取文件reader.readAsText(file);}
};

字符串操作说明

  • FileReader.readAsText() 将文件内容读取为字符串
  • JSON.parse() 将JSON字符串解析为JavaScript对象
  • 注意需要使用 try-catch 来捕获可能的JSON解析错误

六、文件移除功能实现

实现文件移除功能,清空相关状态:

const handleRemove = () => {setFileList([]);setSelectedFile(null);setPreviewImage('');setJsonContent(null);return true;
};

七、集成Ant Design Upload组件

将以上功能集成到Ant Design的Upload.Dragger组件中:

// 定义上传配置
const uploadProps: UploadProps = {name: 'file',multiple: false,fileList,beforeUpload,onRemove: handleRemove,customRequest: () => {}, // 自定义上传逻辑,这里为空实现showUploadList: false, // 隐藏默认的上传列表,我们将自定义显示
};// 在组件的返回部分添加Dragger组件
return (<div style={{ maxWidth: 600, margin: '0 auto', padding: 20 }}><h2>文件上传示例</h2><Dragger {...uploadProps}><p className="ant-upload-drag-icon"><InboxOutlined /></p><p className="ant-upload-text">点击或拖拽文件到此区域上传</p><p className="ant-upload-hint">支持单个JSON或图片文件上传,最大5MB</p></Dragger>{/* 文件信息显示区域将在后续添加 */}</div>
);

注意事项

  • 设置 multiple: false 确保一次只能上传一个文件
  • 设置 showUploadList: false 隐藏默认上传列表,以便我们自定义显示方式
  • 添加空的 customRequest 函数覆盖默认上传行为

八、自定义UI显示与预览功能

1. 添加文件信息显示

当用户选择文件后,我们显示文件信息并提供预览按钮:

// 在返回的JSX中添加文件信息显示区域
{selectedFile && (<div style={{ marginTop: 20, padding: 16, border: '1px solid #d9d9d9', borderRadius: 4 }}><h3>文件信息</h3><p><strong>文件名:</strong> {selectedFile.name}</p><p><strong>大小:</strong> {(selectedFile.size / 1024).toFixed(2)} KB</p><p><strong>类型:</strong> {selectedFile.type || '未知'}</p><div style={{ marginTop: 16 }}><button onClick={handlePreview} style={{ marginRight: 8, padding: '4px 16px', backgroundColor: '#1890ff', color: 'white', border: 'none', borderRadius: 4 }}>预览</button><button onClick={handleRemove} style={{ padding: '4px 16px', backgroundColor: '#fff', color: '#d9d9d9', border: '1px solid #d9d9d9', borderRadius: 4 }}>移除</button></div></div>
)}

2. 添加预览弹窗

为图片和JSON文件添加预览弹窗:

// 导入Modal组件
import { Upload, message, Modal } from 'antd';// 在返回的JSX中添加预览弹窗
<Modalopen={previewOpen}title="图片预览"footer={null}onCancel={() => setPreviewOpen(false)}
><img alt="预览" style={{ width: '100%' }} src={previewImage} />
</Modal><Modalopen={jsonModalOpen}title="JSON内容"width={700}footer={null}onCancel={() => setJsonModalOpen(false)}
><pre style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-all' }}>{JSON.stringify(jsonContent, null, 2)}</pre>
</Modal>

字符串格式化说明

  • JSON.stringify(jsonContent, null, 2) 用于将JSON对象格式化为易读的字符串
  • 第二个参数 null 表示不需要过滤任何属性
  • 第三个参数 2 表示使用两个空格进行缩进

九、完整代码实现

综合以上所有功能,我们的完整代码如下:

import React, { useState } from "react";
import { Upload, Button, Typography, message, Modal, Tag, Card } from "antd";
import { CodeOutlined, FileOutlined, InboxOutlined } from "@ant-design/icons";
// typography 排版组件 需要展示标题段落列表文本样式
// Upload上传组件 用于上传文件(图片、json文件等)
const { Text, Title } = Typography;
const { Dragger } = Upload;
// 上传json文件的小案例
/*文件上传演示组件支持json和图片文件上传 并且提供预览功能*/
export default function FileUploadDemo() {//我们将在这里添加状态和功能const [fileList, setFileList] = useState<File[]>([]);const [selectedFile, setSelectedFile] = useState<File | null>(null);//图片预览相关状态const [previewOpen, setPreviewOpen] = useState(false);const [previewImage, setPreviewImage] = useState("");const [previewTitle, setPreviewTitle] = useState("");//json文件预览相关状态const [jsonContent, setJsonContent] = useState<any>("");const [jsonPreviewOpen, setJsonPreviewOpen] = useState(false);// 这些状态将帮助我们跟踪用户选择的文件//文件类型验证const beforeUpload = (file: File) => {//获取文件类型const isJSON =file.type === "application/json" || file.name.endsWith(".json");const isImage = file.type.startsWith("image/");//限制文件格式if (!isJSON && !isImage) {message.error("仅支持上传json和图片文件");return false;}//限制文件大小const isLt5M = file.size / 1024 / 1024 < 5;if (!isLt5M) {message.error("文件大小不能超过5MB");return false;}//只允许选择单个文件if (fileList.length >= 1) {message.error("只能上传一个文件");return false;}//处理文件handleFile(file);return false; //防止自动上传};//处理文件const handleFile = (file: File) => {const newFileList = [file];setFileList(newFileList);setSelectedFile(file);//如果是图片 设置预览if (file.type.startsWith("image/")) {const render = new FileReader();render.readAsDataURL(file);//将图片文件转换为base64编码的字符串render.onload = () => {setPreviewImage(render.result as string);};//读取完成后将结果转化为字符串保存到previewImage状态中setJsonContent(null);} else if (file.type === "application/json" ||file.name.endsWith(".json")) {const reader = new FileReader();reader.readAsText(file, "utf-8");//将json文件转换为字符串reader.onload = (evt) => {try {const content = evt.target?.result as string;const parsedJson = JSON.parse(content);setJsonContent(parsedJson);} catch (error) {message.error("json文件格式错误");setJsonContent(null);}};//读取完成后将结果转化为字符串保存到jsonContent状态中setPreviewImage("");}};//预览图片const handlePreview = () => {if (selectedFile?.type.startsWith("image/")) {setPreviewTitle(selectedFile.name);setPreviewOpen(true);}};//预览json文件const handleJsonPreview = () => {if (jsonContent) {setJsonPreviewOpen(true);}};//格式化JSON显示const formatJson = (json: any): string => {return JSON.stringify(json, null, 2);};//移除文件const handleRemove = () => {setFileList([]);setSelectedFile(null);setPreviewImage("");};//上传配置const uploadProps = {name: "file",//像后端表单上传时的字段名multiple: false,//是否允许 一个选或者拖拽多个文件 false只允许一个fileList: fileList.map((file) => ({uid: Date.now().toString(),name: file.name,status: "done",url: file.type.startsWith("image/") ? previewImage : undefined,})),beforeUpload,onRemove: handleRemove,onDrop: (e) => {if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {handleFile(e.dataTransfer.files[0]);}},onPreview: handlePreview,//拖拽的话也执行处理文件showUploadList: true,customRequest: () => {}, //阻止自动上传};return (<div style={{ maxWidth: "800px", margin: "0 auto" }}><Title level={2}>文件上传演示</Title><Text type="secondary">支持上传json和图片文件</Text><div style={{ marginTop: "24px" }}><Dragger {...uploadProps}><p className="ant-upload-drag-icon"><InboxOutlined /></p><p className="ant-upload-text">点击或拖拽文件到这里上传</p><p className="ant-upload-hint">仅支持上传json和图片文件</p></Dragger></div>{/* 显示文件信息和操作按钮 */}{selectedFile && (<Cardtitle="文件信息"style={{ marginTop: 16 }}extra={selectedFile.type.startsWith("image/") ? (<Buttontype="primary"icon={<FileOutlined />}onClick={handlePreview}>预览图片</Button>) : jsonContent !== null ? (<Buttontype="primary"icon={<CodeOutlined />}onClick={handleJsonPreview}>查看JSON内容</Button>) : null}><p><strong>文件名:</strong>{selectedFile.name}</p><p><strong>文件大小:</strong>{(selectedFile.size / 1024).toFixed(2)} KB</p><p><strong>文件类型:</strong>{selectedFile.type.startsWith("image/") ? (<Tag color="blue">图片</Tag>) : (<Tag color="green">JSON</Tag>)}</p></Card>)}<Modalopen={previewOpen}onCancel={() => setPreviewOpen(false)}title={previewTitle}footer={null}><img src={previewImage} alt="预览" style={{ width: "100%" }} /></Modal><Modalopen={jsonPreviewOpen}onCancel={() => setJsonPreviewOpen(false)}title="JSON文件预览"footer={null}><pre>{formatJson(jsonContent)}</pre></Modal></div>);
}

效果图

http://www.dtcms.com/a/465845.html

相关文章:

  • 第五部分:VTK高级功能模块(第140章 Accelerators模块 - 加速器支持类)
  • 头条站长平台电商网站开发实训软件
  • 库卡机械臂的转角系统以及固定轴和欧拉角的计算方式
  • 机器学习高级-Chapter 04-概率论与贝叶斯分类
  • 站点搜索编程零基础入门课程
  • 网站焦点图如何美观python做网站怎么样
  • SQL百题斩:从入门到精通,一站式解锁数据世界
  • TMC2240步进电机驱动芯片寄存器配置和电路设计
  • 高通被调查市占率将降,昂瑞微IPO推动射频芯片自主创新
  • SQL SERVER从专家到小白
  • 温州市企业网站制作网站升级建设中
  • 开源安全管理平台wazuh-暴力破解检测与响应
  • 视频网站用虚拟主机织梦网站标题被篡改
  • VBA即用型代码手册:创建一个新文档并保存html文件
  • 深圳模板网站多少钱音乐外链网站
  • Java 数学类详解:Math 类常用方法(abs/sqrt/random 等)一篇掌握
  • html 全角空格和半角空格
  • 网络协议之文件下载相关协议
  • 图数据库neo4j desktop2.0初探
  • 广州网站建设网络推广公司酒店旅游团购网站建设
  • 重庆镇海seo整站优化价格紫色个人网站模板
  • 有安全好用且稳定的共享网盘吗?
  • 网站空间是不是服务器南宁网站建设怎样建立一个好网站
  • 网站建设系统认证系统为什么建设银行的网站打不开
  • 并行传输如何重塑数据交换新格局
  • Maven用户设置文件(settings.xml)配置指南
  • 闪电卫士:弧光保护装置以毫秒速度守护电力安全
  • 免费搭建网站注册网站什么要求
  • CCF-GESP 等级考试 2025年9月认证C++三级真题解析
  • Wireshark 过滤器命令大汇总:从入门到精通