前端文件上传方案:别再只用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

跨语言翻译微调实战:使用Llama-Factory训练多语种模型

跨语言翻译微调实战:使用Llama-Factory训练多语种模型 在当今全球化数字生态中,自动翻译系统早已不再是简单的“词对词”替换工具,而是支撑跨境电商、跨国协作和跨文化传播的核心基础设施。然而,通用大模型在面对专业术语密集或低资源语言组合(如中文→斯瓦希里语)时,常常暴露出语义失真、风格不一致等问题。传统解决方案依赖庞大的双语语料库与昂贵的计算资源,使得中小企业和独立开发者望而却步。 有没有一种方式,能让一台配备RTX 3090的工作站,在几天内就完成一个高质量中英术语翻译模型的定制化训练?答案是肯定的——借助 LLama-Factory 这类一站式微调框架,结合参数高效微调技术,我们正进入“平民化大模型定制”的新时代。 LLama-Factory 并非从零构建的训练脚本集合,而是一个面向真实工程场景深度打磨的完整工具链。它的价值不仅体现在支持 LLaMA、Qwen、Baichuan 等上百种主流开源架构的统一接口上,更在于它将原本分散在数十个 GitHub 仓库中的最佳实践整合为一条可复用、可扩展的流水线。无论是数据预处理、分布式训练,还是量化部署,开发者都可以通过命令行或

文墨共鸣多场景:同时支持短文本比对(标题)、中长文本(段落)、长文本(章节)

文墨共鸣多场景:同时支持短文本比对(标题)、中长文本(段落)、长文本(章节) "夫文心者,言为心声,义为神合。" 文墨共鸣将深度学习算法与传统水墨美学完美融合,基于StructBERT技术,精准识别文字间的微妙关联,无论是标题、段落还是章节,都能准确判断是"异曲同工"还是"云泥之别"。 1. 项目概览:当AI遇见水墨艺术 文墨共鸣是一个创新的语义相似度分析系统,它将先进的自然语言处理技术与典雅的中国传统美学相结合。不同于传统的技术工具,文墨共鸣在准确分析文本相似度的同时,为用户带来沉浸式的文化体验。 这个系统的核心价值在于其多场景适配能力:从几个字的标题比对,到数百字的中长段落分析,再到数千字的长章节对比,都能提供精准的语义相似度判断。无论是学术研究、内容创作还是日常办公,都能找到适用的场景。 2. 核心功能:全场景文本比对 2.1 短文本比对:精准捕捉标题精髓 短文本比对专门处理标题、标语、

终极Stable Diffusion WebUI Forge模型评估实战指南:三大核心指标深度解析与应用技巧

终极Stable Diffusion WebUI Forge模型评估实战指南:三大核心指标深度解析与应用技巧 【免费下载链接】stable-diffusion-webui-forge 项目地址: https://gitcode.com/GitHub_Trending/st/stable-diffusion-webui-forge Stable Diffusion WebUI Forge是一款功能强大的AI绘图工具,它为用户提供了丰富的模型选择和灵活的参数配置,帮助创作者轻松生成高质量图像。本文将深入解析模型评估的三大核心指标,为新手用户提供一套完整的模型评估实战指南,让你能够快速判断模型性能,选择最适合自己需求的AI绘图模型。 模型评估前的准备工作 在开始评估模型之前,我们需要先准备好必要的工具和环境。首先,确保你已经正确安装了Stable Diffusion WebUI Forge。如果你还没有安装,可以通过以下命令克隆仓库并进行安装: git clone https://gitcode.com/GitHub_Trending/st/stable-diffusion-we

生成式人工智能(AIGC)在开放式教育问答系统中的知识表征与推理机制研究

一、引言 (一)研究背景与意义 在数字化浪潮席卷全球的当下,教育领域正经历着深刻的变革,教育数字化转型进程显著加速。这一转型不仅是技术层面的更新换代,更是教育理念、教学模式以及知识传播与获取方式的全面革新。开放式教育问答系统作为教育数字化的关键支撑,在满足学习者多样化学习需求方面发挥着日益重要的作用。它打破了传统教育在时间和空间上的限制,为学习者提供了随时随地获取知识的便捷途径,使得学习不再受限于课堂和教材,极大地拓展了教育的边界。 随着知识的快速更新和学科交叉融合的不断深入,开放式教育问答系统面临着前所未有的挑战。它需要处理来自多领域、多学科的知识,并且要适应知识动态化发展的趋势,满足学习者在不同学习场景下的知识交互需求。例如,在跨学科研究中,学习者可能需要同时了解物理学、化学、生物学等多个学科的知识,并探索它们之间的内在联系;在解决实际问题时,学习者需要将理论知识与实际应用相结合,获取具有针对性的解决方案。传统的知识表征与推理机制在应对这些复杂多变的需求时,逐渐显露出其固有的局限性。 从灵活性角度来看,传统机制往往采用预先设定的规则和框架来表示知识,缺乏对新知识和新情况的自