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

QGIS:Maxar Open Data全球高分辨率遥感影像(0.3-0.5米)14TB免费获取

QGIS:Maxar Open Data全球高分辨率遥感影像(0.3-0.5米)14TB免费获取

今天给大家介绍一个插件Maxar Open Data QGIS Plugin,它是一个用于浏览、可视化和下载 Maxar 开放数据卫星图像的 QGIS 插件,用于灾害事件。 简介 Maxar Open Data 计划是全球领先的卫星影像服务商 Maxar Technologies 针对重大突发灾害发起的公益性数据共享项目。该项目旨在通过提供亚米级高分辨率的光学卫星影像,帮助人道主义组织、政府机构和一线救援人员更有效地进行灾情评估、资源调度及灾后重建工作。这些数据通常涵盖地震、洪水、野火及飓风等突发性自然灾害前后的对比图,为全球范围内的灾害响应提供关键的时间序列地理空间支持。 该数据集主要由 Maxar 旗下的高分辨率星座(如 WorldView-1/2/3 和 GeoEye-1)捕获,其空间分辨率可达 30cm 至 50cm,能够清晰识别建筑物损毁、道路阻塞及难民营规模。在灾害发生后的极短时间内,Maxar 会迅速将受灾区域的影像处理为分析就绪数据(ARD),并发布在专用的云平台上,供全球用户免费下载和使用。

利用百川2-13B构建AIGC内容安全审核Agent实战

利用百川2-13B构建AIGC内容安全审核Agent实战 最近跟几个做内容社区和社交产品的朋友聊天,他们都在头疼同一个问题:用户生成的内容(UGC)越来越多,尤其是现在AIGC工具普及了,用户一天能生产出海量的帖子、评论、脚本,人工审核根本看不过来。更麻烦的是,里面可能夹杂着一些不合规的内容,轻则影响社区氛围,重则可能引发风险。 传统的审核规则库更新慢,面对层出不穷的新梗、黑话常常失灵。纯人工审核呢,成本高、效率低,还容易因为疲劳导致误判。有没有一种更“聪明”的办法?我们尝试用百川2-13B大模型,搭建了一个智能的内容安全审核Agent。它不只是一个简单的“是/否”过滤器,更像一个懂规则、能推理的“审核专员”,不仅能识别风险,还能给出修改建议。今天,我就把这个从架构设计到部署上线的实战过程,跟大家分享一下。 1. 为什么需要“智能”审核Agent? 先说说我们面临的几个核心痛点,可能也是很多平台正在经历的。 第一,审核维度复杂且动态变化。 内容安全不只是屏蔽几个敏感词那么简单。它涉及多个维度:政治敏感、

Copilot、Codeium 软件开发领域的代表性工具背后的技术

Copilot、Codeium 软件开发领域的代表性工具背后的技术

早期, Claude、Copilot、Codeium新兴的AI代码助手,模型的温度、切片的效果、检索方式、提示词的约束、AI 回复的约束、最终数据处理;整个环节,任何一个地方都可能造成最终效果不理想。 旨在通过代码生成、代码补全、代码解释和调试等多种功能,帮助开发者减少重复劳动,提高开发效率。尽管Codeium已经取得了显著的成果,但在处理复杂的代码任务、跨文件的修改以及支持定制化库和框架方面仍面临一定的局限性。 2020 年,OpenAI发布的GPT-3模型使AI生成代码的能力得以广泛应用,标志着AI代码助手的转型。2021年,GitHub 推出基于OpenAI Codex的 Copilot,提供实时代码补全和生成能力,提升开发效率,支持跨文件复杂任务。 其痛点,在大规模代码生成、跨文件任务处理以及定制化框架支持方面的局限性仍然限制了其在复杂项目中的应用。 2023年,Claude 3.5等新一代大型语言模型陆续出世,有效提升了自然语言理解与代码生成的能力。这类模型集成了代码生成、调试和文档自动生成等多项功能,能够帮助开发者快速编写高质量代码、优化程序性能并自动修复错误。随着

VLM经典论文阅读:【综述】An Introduction to Vision-Language Modeling

VLM经典论文阅读:【综述】An Introduction to Vision-Language Modeling

VLM经典论文阅读:【综述】An Introduction to Vision-Language Modeling * 【前言】论文简介 🍀 * 1、介绍(Introduction)🐳 * 2、视觉语言模型家族(The Families of VLMs) 🌟 * 2.1 基于Transformer的早期VLM工作(Early work on VLMs based on transformers) * 2.2 基于对比学习的VLM(Contrastive-based VLMs) * 2.2.1 CLIP * 2.3 掩码目标视觉语言模型(VLMs with masking objectives) * 2.3.1 FLAVA * 2.3.