React 文件上传组件 File Upload

本文涉及的产品
可观测可视化 Grafana 版,10个用户账号 1个月
可观测监控 Prometheus 版,每月50GB免费额度
云原生网关 MSE Higress,422元/月
简介: 本文详细介绍了如何在 React 中实现文件上传组件,从基础的文件选择和上传到服务器,再到解决文件大小、类型限制、并发上传等问题,以及实现多文件上传、断点续传和文件预览等高级功能,帮助开发者高效构建可靠的应用。

引言

文件上传是现代 Web 应用中不可或缺的功能之一。无论是用户头像、文档附件还是多媒体文件,都需要一个高效且可靠的文件上传组件来处理。React 作为当前最流行的前端框架之一,提供了丰富的工具和库来简化文件上传的实现。本文将从基础开始,逐步深入介绍如何在 React 中实现文件上传组件,并探讨一些常见的问题、易错点及如何避免这些问题。
image.png

基础实现

1. 创建基本的文件上传组件

首先,我们创建一个简单的文件上传组件,使用 HTML 的 <input type="file"> 元素来选择文件。

import React, { useState } from 'react';

const FileUpload = () => {
  const [file, setFile] = useState(null);

  const handleFileChange = (event) => {
    setFile(event.target.files[0]);
  };

  const handleUpload = () => {
    if (file) {
      console.log('Selected file:', file);
    } else {
      console.log('No file selected');
    }
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
      <button onClick={handleUpload}>Upload</button>
    </div>
  );
};

export default FileUpload;

2. 发送文件到服务器

接下来,我们将文件发送到服务器。这里使用 fetch API 来实现。

import React, { useState } from 'react';

const FileUpload = () => {
  const [file, setFile] = useState(null);

  const handleFileChange = (event) => {
    setFile(event.target.files[0]);
  };

  const handleUpload = async () => {
    if (!file) {
      alert('Please select a file first');
      return;
    }

    const formData = new FormData();
    formData.append('file', file);

    try {
      const response = await fetch('https://api.example.com/upload', {
        method: 'POST',
        body: formData,
      });

      if (response.ok) {
        const data = await response.json();
        console.log('File uploaded successfully:', data);
      } else {
        console.error('File upload failed');
      }
    } catch (error) {
      console.error('Error uploading file:', error);
    }
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
      <button onClick={handleUpload}>Upload</button>
    </div>
  );
};

export default FileUpload;

常见问题及易错点

1. 文件大小限制

  • 问题:用户上传的文件过大,导致请求失败。
  • 解决方法:在前端进行文件大小校验,并在后端设置文件大小限制。
const handleFileChange = (event) => {
  const selectedFile = event.target.files[0];
  const maxSize = 5 * 1024 * 1024; // 5MB

  if (selectedFile.size > maxSize) {
    alert('File size exceeds the limit of 5MB');
    return;
  }

  setFile(selectedFile);
};

2. 文件类型限制

  • 问题:用户上传了不支持的文件类型。
  • 解决方法:在前端进行文件类型校验,并在后端设置文件类型限制。
const handleFileChange = (event) => {
  const selectedFile = event.target.files[0];
  const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];

  if (!allowedTypes.includes(selectedFile.type)) {
    alert('Unsupported file type. Please upload a JPEG, PNG, or PDF file.');
    return;
  }

  setFile(selectedFile);
};

3. 并发上传

  • 问题:用户同时上传多个文件,导致服务器压力增大。
  • 解决方法:限制同时上传的文件数量,或者使用队列机制逐个上传文件。
const [files, setFiles] = useState([]);

const handleFileChange = (event) => {
  const selectedFiles = Array.from(event.target.files);
  const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
  const maxSize = 5 * 1024 * 1024; // 5MB

  const validFiles = selectedFiles.filter(file => allowedTypes.includes(file.type) && file.size <= maxSize);

  if (validFiles.length === 0) {
    alert('No valid files selected');
    return;
  }

  setFiles([...files, ...validFiles]);
};

const handleUpload = async () => {
  if (files.length === 0) {
    alert('Please select at least one file');
    return;
  }

  for (const file of files) {
    const formData = new FormData();
    formData.append('file', file);

    try {
      const response = await fetch('https://api.example.com/upload', {
        method: 'POST',
        body: formData,
      });

      if (response.ok) {
        const data = await response.json();
        console.log('File uploaded successfully:', data);
      } else {
        console.error('File upload failed');
      }
    } catch (error) {
      console.error('Error uploading file:', error);
    }
  }

  setFiles([]);
};

4. 进度条显示

  • 问题:用户无法知道文件上传的进度。
  • 解决方法:使用 fetchonUploadProgress 回调来显示上传进度。
import React, { useState } from 'react';
import axios from 'axios';

const FileUpload = () => {
  const [file, setFile] = useState(null);
  const [progress, setProgress] = useState(0);

  const handleFileChange = (event) => {
    const selectedFile = event.target.files[0];
    const maxSize = 5 * 1024 * 1024; // 5MB

    if (selectedFile.size > maxSize) {
      alert('File size exceeds the limit of 5MB');
      return;
    }

    setFile(selectedFile);
  };

  const handleUpload = async () => {
    if (!file) {
      alert('Please select a file first');
      return;
    }

    const formData = new FormData();
    formData.append('file', file);

    try {
      const response = await axios.post('https://api.example.com/upload', formData, {
        onUploadProgress: (progressEvent) => {
          const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
          setProgress(percentCompleted);
        },
      });

      if (response.status === 200) {
        console.log('File uploaded successfully:', response.data);
      } else {
        console.error('File upload failed');
      }
    } catch (error) {
      console.error('Error uploading file:', error);
    }
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
      <button onClick={handleUpload}>Upload</button>
      {progress > 0 && <p>Upload Progress: {progress}%</p>}
    </div>
  );
};

export default FileUpload;

高级功能与优化

1. 多文件上传

在实际应用中,用户可能需要一次上传多个文件。我们可以使用 multiple 属性来允许用户选择多个文件,并批量上传。

import React, { useState } from 'react';
import axios from 'axios';

const MultiFileUpload = () => {
  const [files, setFiles] = useState([]);
  const [progress, setProgress] = useState(0);

  const handleFileChange = (event) => {
    const selectedFiles = Array.from(event.target.files);
    const maxSize = 5 * 1024 * 1024; // 5MB
    const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];

    const validFiles = selectedFiles.filter(file => allowedTypes.includes(file.type) && file.size <= maxSize);

    if (validFiles.length === 0) {
      alert('No valid files selected');
      return;
    }

    setFiles([...files, ...validFiles]);
  };

  const handleUpload = async () => {
    if (files.length === 0) {
      alert('Please select at least one file');
      return;
    }

    for (const file of files) {
      const formData = new FormData();
      formData.append('file', file);

      try {
        const response = await axios.post('https://api.example.com/upload', formData, {
          onUploadProgress: (progressEvent) => {
            const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
            setProgress(percentCompleted);
          },
        });

        if (response.status === 200) {
          console.log('File uploaded successfully:', response.data);
        } else {
          console.error('File upload failed');
        }
      } catch (error) {
        console.error('Error uploading file:', error);
      }
    }

    setFiles([]);
  };

  return (
    <div>
      <input type="file" multiple onChange={handleFileChange} />
      <button onClick={handleUpload}>Upload</button>
      {progress > 0 && <p>Upload Progress: {progress}%</p>}
    </div>
  );
};

export default MultiFileUpload;

2. 断点续传

  • 问题:大文件上传过程中网络中断,导致上传失败。
  • 解决方法:实现断点续传功能,允许用户在网络恢复后继续上传未完成的部分。
import React, { useState } from 'react';
import axios from 'axios';

const ResumableFileUpload = () => {
  const [file, setFile] = useState(null);
  const [progress, setProgress] = useState(0);

  const handleFileChange = (event) => {
    setFile(event.target.files[0]);
  };

  const handleUpload = async () => {
    if (!file) {
      alert('Please select a file first');
      return;
    }

    const chunkSize = 1024 * 1024; // 1MB
    const totalChunks = Math.ceil(file.size / chunkSize);

    for (let i = 0; i < totalChunks; i++) {
      const start = i * chunkSize;
      const end = Math.min(start + chunkSize, file.size);
      const chunk = file.slice(start, end);

      const formData = new FormData();
      formData.append('file', chunk);
      formData.append('chunkIndex', i);
      formData.append('totalChunks', totalChunks);

      try {
        const response = await axios.post('https://api.example.com/upload', formData, {
          onUploadProgress: (progressEvent) => {
            const percentCompleted = Math.round(((i * chunkSize + progressEvent.loaded) * 100) / file.size);
            setProgress(percentCompleted);
          },
        });

        if (response.status === 200) {
          console.log(`Chunk ${i + 1}/${totalChunks} uploaded successfully`);
        } else {
          console.error(`Chunk ${i + 1}/${totalChunks} upload failed`);
        }
      } catch (error) {
        console.error(`Error uploading chunk ${i + 1}/${totalChunks}:`, error);
      }
    }

    setProgress(100);
    alert('File uploaded successfully');
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
      <button onClick={handleUpload}>Upload</button>
      {progress > 0 && <p>Upload Progress: {progress}%</p>}
    </div>
  );
};

export default ResumableFileUpload;

3. 文件预览

  • 问题:用户无法预览上传的文件,特别是图片文件。
  • 解决方法:在前端生成文件的预览 URL,并显示给用户。
import React, { useState } from 'react';

const FilePreviewUpload = () => {
  const [file, setFile] = useState(null);
  const [previewUrl, setPreviewUrl] = useState('');

  const handleFileChange = (event) => {
    const selectedFile = event.target.files[0];

    if (selectedFile) {
      const reader = new FileReader();
      reader.onloadend = () => {
        setPreviewUrl(reader.result);
      };
      reader.readAsDataURL(selectedFile);
      setFile(selectedFile);
    }
  };

  const handleUpload = () => {
    if (file) {
      console.log('Selected file:', file);
    } else {
      console.log('No file selected');
    }
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
      <button onClick={handleUpload}>Upload</button>
      {previewUrl && <img src={previewUrl} alt="Preview" style={
  { maxWidth: '100px' }} />}
    </div>
  );
};

export default FilePreviewUpload;

总结

通过本文的介绍,我们从基础的文件上传组件开始,逐步深入探讨了常见的问题、易错点及如何避免这些问题。我们还介绍了多文件上传、断点续传和文件预览等高级功能,希望这些内容能帮助你在实际开发中更加顺利地实现文件上传功能。

如果你有任何疑问或建议,欢迎留言交流!

目录
相关文章
|
2天前
|
人工智能 自动驾驶 大数据
预告 | 阿里云邀您参加2024中国生成式AI大会上海站,马上报名
大会以“智能跃进 创造无限”为主题,设置主会场峰会、分会场研讨会及展览区,聚焦大模型、AI Infra等热点议题。阿里云智算集群产品解决方案负责人丛培岩将出席并发表《高性能智算集群设计思考与实践》主题演讲。观众报名现已开放。
|
19天前
|
存储 人工智能 弹性计算
阿里云弹性计算_加速计算专场精华概览 | 2024云栖大会回顾
2024年9月19-21日,2024云栖大会在杭州云栖小镇举行,阿里云智能集团资深技术专家、异构计算产品技术负责人王超等多位产品、技术专家,共同带来了题为《AI Infra的前沿技术与应用实践》的专场session。本次专场重点介绍了阿里云AI Infra 产品架构与技术能力,及用户如何使用阿里云灵骏产品进行AI大模型开发、训练和应用。围绕当下大模型训练和推理的技术难点,专家们分享了如何在阿里云上实现稳定、高效、经济的大模型训练,并通过多个客户案例展示了云上大模型训练的显著优势。
|
22天前
|
存储 人工智能 调度
阿里云吴结生:高性能计算持续创新,响应数据+AI时代的多元化负载需求
在数字化转型的大潮中,每家公司都在积极探索如何利用数据驱动业务增长,而AI技术的快速发展更是加速了这一进程。
|
13天前
|
并行计算 前端开发 物联网
全网首发!真·从0到1!万字长文带你入门Qwen2.5-Coder——介绍、体验、本地部署及简单微调
2024年11月12日,阿里云通义大模型团队正式开源通义千问代码模型全系列,包括6款Qwen2.5-Coder模型,每个规模包含Base和Instruct两个版本。其中32B尺寸的旗舰代码模型在多项基准评测中取得开源最佳成绩,成为全球最强开源代码模型,多项关键能力超越GPT-4o。Qwen2.5-Coder具备强大、多样和实用等优点,通过持续训练,结合源代码、文本代码混合数据及合成数据,显著提升了代码生成、推理和修复等核心任务的性能。此外,该模型还支持多种编程语言,并在人类偏好对齐方面表现出色。本文为周周的奇妙编程原创,阿里云社区首发,未经同意不得转载。
|
7天前
|
人工智能 自然语言处理 前端开发
100个降噪蓝牙耳机免费领,用通义灵码从 0 开始打造一个完整APP
打开手机,录制下你完成的代码效果,发布到你的社交媒体,前 100 个@玺哥超Carry、@通义灵码的粉丝,可以免费获得一个降噪蓝牙耳机。
3812 13
|
14天前
|
人工智能 自然语言处理 前端开发
用通义灵码,从 0 开始打造一个完整APP,无需编程经验就可以完成
通义灵码携手科技博主@玺哥超carry 打造全网第一个完整的、面向普通人的自然语言编程教程。完全使用 AI,再配合简单易懂的方法,只要你会打字,就能真正做出一个完整的应用。本教程完全免费,而且为大家准备了 100 个降噪蓝牙耳机,送给前 100 个完成的粉丝。获奖的方式非常简单,只要你跟着教程完成第一课的内容就能获得。
6293 10
|
26天前
|
缓存 监控 Linux
Python 实时获取Linux服务器信息
Python 实时获取Linux服务器信息
|
12天前
|
人工智能 自然语言处理 前端开发
什么?!通义千问也可以在线开发应用了?!
阿里巴巴推出的通义千问,是一个超大规模语言模型,旨在高效处理信息和生成创意内容。它不仅能在创意文案、办公助理、学习助手等领域提供丰富交互体验,还支持定制化解决方案。近日,通义千问推出代码模式,基于Qwen2.5-Coder模型,用户即使不懂编程也能用自然语言生成应用,如个人简历、2048小游戏等。该模式通过预置模板和灵活的自定义选项,极大简化了应用开发过程,助力用户快速实现创意。
|
22小时前
|
机器学习/深度学习 人工智能 安全
通义千问开源的QwQ模型,一个会思考的AI,百炼邀您第一时间体验
Qwen团队推出新成员QwQ-32B-Preview,专注于增强AI推理能力。通过深入探索和试验,该模型在数学和编程领域展现了卓越的理解力,但仍在学习和完善中。目前,QwQ-32B-Preview已上线阿里云百炼平台,提供免费体验。
|
8天前
|
人工智能 C++ iOS开发
ollama + qwen2.5-coder + VS Code + Continue 实现本地AI 辅助写代码
本文介绍在Apple M4 MacOS环境下搭建Ollama和qwen2.5-coder模型的过程。首先通过官网或Brew安装Ollama,然后下载qwen2.5-coder模型,可通过终端命令`ollama run qwen2.5-coder`启动模型进行测试。最后,在VS Code中安装Continue插件,并配置qwen2.5-coder模型用于代码开发辅助。
629 4