前端文件上传方案:别再只用input type=file了

前端文件上传方案:别再只用input type=file了

前端文件上传方案:别再只用input type=file了

毒舌时刻

这代码写得跟网红滤镜似的——仅供参考。

各位前端同行,咱们今天聊聊前端文件上传。别告诉我你还在用原生的input上传大文件,那感觉就像在用小水管灌满游泳池——慢得让人绝望。

为什么你需要文件上传方案

最近看到一个项目,上传100MB的文件直接卡死浏览器,没有任何进度提示,我差点当场去世。我就想问:你是在做上传还是在做浏览器杀手?

反面教材

<!-- 反面教材:原生文件上传 --> <input type="file" onchange="uploadFile(this.files[0])" /> <script> function uploadFile(file) { const formData = new FormData(); formData.append('file', file); // 直接上传,没有进度,没有断点续传 fetch('/api/upload', { method: 'POST', body: formData }); } </script> 

毒舌点评:这代码,我看了都替你的用户着急。原生上传大文件,你是想让用户等到天荒地老吗?

前端文件上传的正确姿势

1. 分片上传

// 正确姿势:分片上传 class ChunkUploader { constructor(file, options = {}) { this.file = file; this.chunkSize = options.chunkSize || 1024 * 1024; // 1MB this.chunks = Math.ceil(file.size / this.chunkSize); this.uploadedChunks = 0; } async upload() { const promises = []; for (let i = 0; i < this.chunks; i++) { const start = i * this.chunkSize; const end = Math.min(start + this.chunkSize, this.file.size); const chunk = this.file.slice(start, end); promises.push(this.uploadChunk(chunk, i)); } await Promise.all(promises); await this.mergeChunks(); } async uploadChunk(chunk, index) { const formData = new FormData(); formData.append('chunk', chunk); formData.append('index', index); formData.append('total', this.chunks); formData.append('filename', this.file.name); await fetch('/api/upload/chunk', { method: 'POST', body: formData }); this.uploadedChunks++; this.onProgress(this.uploadedChunks / this.chunks); } onProgress(progress) { console.log(`上传进度: ${(progress * 100).toFixed(2)}%`); } async mergeChunks() { await fetch('/api/upload/merge', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ filename: this.file.name, chunks: this.chunks }) }); } } // 使用 const uploader = new ChunkUploader(file, { chunkSize: 1024 * 1024 }); uploader.upload(); 

2. 断点续传

// 正确姿势:断点续传 class ResumableUploader { constructor(file) { this.file = file; this.chunkSize = 1024 * 1024; this.uploadedChunks = new Set(); } async init() { // 获取已上传的分片 const response = await fetch(`/api/upload/status?filename=${this.file.name}`); const { uploadedChunks } = await response.json(); this.uploadedChunks = new Set(uploadedChunks); } async upload() { const chunks = Math.ceil(this.file.size / this.chunkSize); for (let i = 0; i < chunks; i++) { if (this.uploadedChunks.has(i)) { console.log(`分片${i}已上传,跳过`); continue; } const start = i * this.chunkSize; const end = Math.min(start + this.chunkSize, this.file.size); const chunk = this.file.slice(start, end); await this.uploadChunk(chunk, i); this.uploadedChunks.add(i); // 保存进度到本地 localStorage.setItem('uploadProgress', JSON.stringify([...this.uploadedChunks])); } await this.mergeChunks(); localStorage.removeItem('uploadProgress'); } async uploadChunk(chunk, index) { // 上传逻辑... } } 

3. 拖拽上传

// 正确姿势:拖拽上传 import { useCallback } from 'react'; function DragUpload({ onUpload }) { const handleDrop = useCallback((e) => { e.preventDefault(); const files = Array.from(e.dataTransfer.files); files.forEach(file => onUpload(file)); }, [onUpload]); const handleDragOver = useCallback((e) => { e.preventDefault(); }, []); return ( <div className="drag-upload" onDrop={handleDrop} onDragOver={handleDragOver} > <p>拖拽文件到此处上传</p> <input type="file" multiple onChange={(e) => { Array.from(e.target.files).forEach(file => onUpload(file)); }} /> </div> ); } 

毒舌点评:早这么写,你的上传早就做好了。别告诉我你还在用原生input,那你还是趁早去用FTP吧。

实战技巧:文件上传指南

1. 上传优化策略

  1. 分片上传:大文件切分上传
  2. 断点续传:支持暂停恢复
  3. 并发控制:限制同时上传数量
  4. 进度显示:实时显示上传进度

2. 最佳实践

// ✅ 显示上传进度 const xhr = new XMLHttpRequest(); xhr.upload.onprogress = (e) => { const progress = (e.loaded / e.total) * 100; console.log(`${progress}%`); }; // ✅ 图片预览 const preview = URL.createObjectURL(file); // ✅ 文件类型检查 const allowedTypes = ['image/jpeg', 'image/png']; if (!allowedTypes.includes(file.type)) { alert('不支持的文件类型'); return; } 

最后想说的

文件上传不是小事,是用户体验的关键。别再只用input type=file了——优化一下,你的上传会更专业。

文件上传就像快递,原生input像平邮,优化后的上传像顺丰。别让用户等平邮,给他们顺丰的体验。

Read more

七牛云 Claude AI 完整配置指南

七牛云 Claude AI 完整配置指南

七牛云 Claude AI 完整配置指南 本指南将帮助您完成七牛云账户注册、API Key 获取,以及在 Zed 编辑器中配置 Claude AI 推理服务的全部流程。 🎯 前置准备 在开始之前,请确保您已具备: * 一个有效的电子邮箱 * 已安装的 Zed 编辑器(最新版本)https://zed.dev/ 稳定的网络连接 在这里插入图片描述 📝 第一步:注册七牛云账户 访问注册页面 点击以下链接进入七牛云官方注册页面: https://s.qiniu.com/BfyeU3 按照页面提示使用邮箱完成账户注册并验证。 🔑 第二步:获取 API Key 2.1 进入 API 密钥管理页面 注册完成后,访问七牛云 AI 推理

AI 对话高效输入指令攻略(四):AI+Apache ECharts:生成各种专业图表

AI 对话高效输入指令攻略(四):AI+Apache ECharts:生成各种专业图表

免责声明: 1.本文所提供的所有 AI 使用示例及提示词,仅用于学术写作技巧交流与 AI 功能探索测试,无任何唆使或鼓励利用 AI 抄袭作业、学术造假的意图。 2.文章中提及的内容旨在帮助读者提升与 AI 交互的能力,合理运用 AI 辅助学习和研究,最终成果的原创性与合规性需使用者自行负责。 3.对于读者因不当使用文中内容,违反学术规范、法律法规或造成其他不良后果的情况,本文作者及发布平台不承担任何责任。 目录 前言 技术栈说明 步骤一.介绍 什么是 Apache ECharts? ECharts 的核心优势 步骤二.部署 1.汉化 2.进入示例库 3.选择模型 4.获得代码 5.“喂”AI 步骤三:本地运行与优化

DeerFlow 2.0:字节开源的超级 Agent 框架,让 AI 真正干活

DeerFlow 2.0:字节开源的超级 Agent 框架,让 AI 真正干活 作者:madprinter 日期:2026-03-24 项目地址:https://github.com/bytedance/deer-flow 项目介绍 DeerFlow(Deep Exploration and Efficient Research Flow)是字节跳动开源的一款超级 Agent 框架。2026 年 2 月 28 日,DeerFlow 2.0 正式发布后迅速登顶 GitHub Trending 榜首,目前已有近 40,000 颗星。 与传统的 AI 框架不同,

【Unity-MCP完全指南:从零开始构建AI游戏开发助手】

【Unity-MCP完全指南:从零开始构建AI游戏开发助手】

标题 * Unity-MCP完全指南:从零开始构建AI游戏开发助手 * 前言 * 一、🤔 什么是Unity-MCP? * 1.1 MCP核心概念解析 * 1.2 为什么要用Unity-MCP? * 1.3 主流Unity-MCP工具对比 * 二、🔧 环境准备与安装 * 2.1 前提条件检查清单 * 2.2 安装Unity-MCP包(桥接组件) * 2.3 验证Python环境与uv安装 * 三、⚙️ Unity端配置详解 * 3.1 打开MCP配置面板 * 3.2 启动MCP服务器 * 3.3 验证服务器状态 * 四、🤖 AI客户端配置指南 * 4.1 Trae配置示例 * 4.1.1 添加MCP配置 * 4.1.2