前端文件【上传下载】姿势大全

本文介绍浏览器与服务器之间文件传输的常见方式,涵盖文件的获取、上传、下载全流程,并附带代码示例。
1 浏览器获取用户本地文件
在浏览器中根据不同场景,有多种获取文件的方式。
1.1 点击上传
通过点击文件表单实现上传,最基础、兼容性最好的方式。

<input type="file" id="file" /> <script> const file = document.getElementById('file') file.addEventListener('change', (e) => { const file = e.target.files[0] console.log('🚀 ~ file -->', file) }) </script>
<input type="file" id="dir" webkitdirectory multiple /> <script> const input = document.getElementById('dir'); input.addEventListener('change', (e) => { const files = e.target.files; console.log('🚀 ~ files -->', files) }); </script>

1.2 拖拽上传
直接拖动文件到页面,交互体验好,符合直觉,适合批量文件。

<div id="drop" style="width:300px;height:200px;border:2px dashed #999;"> Drop files here </div> <script> const drop = document.getElementById('drop'); drop.addEventListener('dragover', (e) => { e.preventDefault(); }); drop.addEventListener('drop', (e) => { e.preventDefault(); const files = e.dataTransfer.files; console.log('🚀 ~ files -->', files) }); </script>

1.3 粘贴上传
将剪切板文件粘贴到页面中,交互体验好,效率高,特别适合上传临时截图。

<p>在此页面 Ctrl / Cmd + V 粘贴文件</p> <script> document.addEventListener('paste', (e) => { const items = e.clipboardData.items; for (const item of items) { if (item.kind === 'file') { const file = item.getAsFile(); console.log('🚀 ~ file -->', file) } } }); </script>

1.4 通过媒体设备获取资源
适用于需要捕获摄像头或麦克风媒体资源时的特殊场景。

<button id="btnOpen">打开摄像头</button> <button id="btnShot">截图</button> <video id="video" autoplay playsinline muted></video> <img id="preview" alt="" /> <script> const btnOpen = document.getElementById('btnOpen'); const btnShot = document.getElementById('btnShot'); const video = document.getElementById('video'); const preview = document.getElementById('preview'); let stream = null; btnOpen.addEventListener('click', async () => { stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false }); video.srcObject = stream; }); btnShot.addEventListener('click', async () => { if (!stream) throw new Error('Camera not started'); const track = stream.getVideoTracks()[0]; const imageCapture = new ImageCapture(track); const blob = await imageCapture.takePhoto(); const url = URL.createObjectURL(blob); preview.src = url; console.log('🚀 ~ file -->', new File( [blob], `screenshot-${Date.now()}.png`, { type: 'image/png' } )) }); </script>

1.5 File System Access API
非标准的实验性接口,功能强大,支持读写。

<button id="openFile">打开文件</button> <button id="openFiles">打开多个文件</button> <button id="openDir">打开目录</button> <script> const buttonFile = document.getElementById('openFile'); buttonFile.addEventListener('click', async () => { const [fileHandle] = await window.showOpenFilePicker(); const file = await fileHandle.getFile(); console.log('🚀 ~ file -->', file) }); const buttonFiles = document.getElementById('openFiles'); buttonFiles.addEventListener('click', async () => { const fileHandles = await window.showOpenFilePicker({ multiple: true, }); const files = await Promise.all(fileHandles.map(fileHandle => fileHandle.getFile())); console.log('🚀 ~ files -->', files) }); const buttonDir = document.getElementById('openDir'); buttonDir.addEventListener('click', async () => { const dir = await window.showDirectoryPicker(); const files = []; for await (const entry of dir.values()) { if (entry.kind === 'file') { files.push(await entry.getFile()); } } console.log('🚀 ~ files -->', files) }); </script>

2 浏览器发送文件
拿到File之后,可以使用fetch发送,如果需要显示进度可以使用XMLHttpRequest
请求体通常使用multipart/form-data编码格式,或者直接上传二进制文件数据。

  • Content-Type: multipart/form-data
  • Content-Type: application/octet-stream
  • Content-Type: [mimetype]

2.1 FormData
使用FormData作为请求体,最通用的方式,适配大多数后端框架与对象存储直传。

const fd = new FormData() fd.append('biz', 'avatar') fd.append('file', file) const res = await fetch('/api/upload', { method: 'POST', body: fd, }) if (res.ok) { const data = await res.json() console.log('🚀 ~ data -->', data) }

浏览器会自动将请求编码为multipart/form-data,并在请求头中设置合适的 boundary。

Content-Length 123456 Content-Type multipart/form-data; boundary=----WebKitFormBoundaryA1B2C3D4 ------WebKitFormBoundaryA1B2C3D4 Content-Disposition: form-data; name="biz" avatar ------WebKitFormBoundaryA1B2C3D4 Content-Disposition: form-data; name="file"; filename="avatar.jpg" Content-Type: image/jpeg [binary content of JPG...] ------WebKitFormBoundaryA1B2C3D4--

2.2 二进制流
直接上传裸文件数据,实现简单,服务端无需解析 multipart,直接写入文件。

const res = await fetch('/api/upload', { method: 'POST', headers: { 'Content-Type': file.type || 'application/octet-stream', 'X-Filename': encodeURIComponent(file.name), }, body: file, }) if (res.ok) { const data = await res.json() console.log('🚀 ~ data -->', data) }
Content-Length 123456 Content-Type image/jpeg X-Filename avatar.jpg

3 浏览器下载文件
下载文件主要有两大类:下载服务端资源,或前端生成/操作文件数据。
3.1 基于服务端资源
服务端提供文件下载链接:

Content-Type: image/jpeg Content-Disposition: attachment; filename="avatar.jpg" Transfer-Encoding: chunked

前端可以基于静态<a>标签,动态创建<a>标签,或者通过导航window.location/window.open触发下载:
<

<a href="/api/file-stream">下载</a> <button id="download-stream">按钮下载</button> <button id="nav-stream">导航下载</button> <script> document.getElementById('download-stream').addEventListener('click', () => { const a = document.createElement('a') a.download = 'avatar.jpg' a.click() }) document.getElementById('nav-stream').addEventListener('click', () => { window.location.href = '/api/file-stream' }) </script>

如果服务端没有返回Content-Disposition,浏览器需要通过<a>标签配合download属性下载文件。

<a href="/api/file-stream" download="avatar.jpg">下载</a>

如果服务端有通过Content-Disposition指定文件名,浏览器通过download属性配置的文件名无效。
💡浏览器导航文件链接大致过程

  1. 解析响应头
  2. 根据Content-Type判断 MIME 类型,决定浏览器如何处理文件
  3. 根据Content-Disposition决定显示还是下载
    1. inline→ 默认值,在页面中显示文件内容
    2. attachment→ 下载文件

  4. 下载文件时会根据Content-Length决定是否显示进度条
  5. 接收响应体并写入内存或文件

3.2 基于二进制数据
适用于前端生成文件或者导出 canvas 等场景。

<button id="download">下载文本</button> <button id="downloadCanvas">下载 canvas 图片</button> <canvas id="canvas" width="200" height="200" style="border: 1px solid #ccc"></canvas> <script> document.getElementById('download').addEventListener('click', () => { const blob = new Blob(['Hello world'], { type: 'text/plain' }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = 'hello.txt' a.click() URL.revokeObjectURL(url) }) const canvas = document.getElementById('canvas') const ctx = canvas.getContext('2d') ctx.fillStyle = '#4a90d9' ctx.fillRect(20, 20, 160, 160) ctx.fillStyle = '#fff' ctx.font = '20px sans-serif' ctx.fillText('Canvas', 60, 105) document.getElementById('downloadCanvas').addEventListener('click', () => { canvas.toBlob((blob) => { const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = 'canvas.png' a.click() URL.revokeObjectURL(url) }, 'image/png') }) </script>

3.3 基于 data URL
适用于极小文本数据,特定场景有用。

<button id="download">下载 SVG</button> <script> const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"> <rect fill="#4a90d9" width="200" height="200"/> <text x="100" y="110" text-anchor="middle" fill="#fff" font-size="24">Hello SVG</text> </svg>` document.getElementById('download').addEventListener('click', () => { const dataUrl = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg) const a = document.createElement('a') a.href = dataUrl a.download = 'hello.svg' a.click() }) </script>

3.4 File System Access API
非标准的实验性接口,直接保存到指定位置。

<button id="download">下载文本</button> <script> document.getElementById('download').addEventListener('click', async () => { const handle = await window.showSaveFilePicker({ suggestedName: 'data.txt', }) const writable = await handle.createWritable() await writable.write('Hello World!') await writable.close() }) </script>

4 服务端返回文件
NodeJS一般可以通过BufferStream的方式返回文件。
4.1 非流式下载(buffered download)
同步阻塞,服务器将整个文件读入内存。

ctx.body = fs.readFileSync('girl.jpg')

服务端会返回Content-Length,浏览器可以据此显示下载进度。
4.2 流式下载(streaming download)
异步文件流,分块读取,适合大文件。

ctx.body = fs.createReadStream('girl.jpg')

流式传输不适合使用Content-Length,所以不需要返回。
💡流式传输时 HTTP/1.1 和 HTTP/2 区别
HTTP/1.1 默认使用分块传输Transfer-Encoding: chunked。因为 HTTP/1.1 使用纯文本 + 字节流的格式实现流式传输,浏览器要么提前知道Content-Length,要么在服务器主动关闭连接时才算结束。当响应长度“不可能提前知道”时,就只能靠chunked解决:遇到 chunk-size 为 0 时即整个响应结束。
HTTP/2 的数据帧机制知道什么时候传输结束,因此不支持chunked
📝 总结
本文介绍了浏览器与服务器之间文件传输的完整流程:

  • 获取文件:支持表单上传、拖拽、粘贴、设备采集和 File System Access API 等多种方式
  • 发送文件:FormData 最通用,二进制流更高效
  • 下载文件:服务器链接、Blob 对象、data URL 和 File System Access API 各有适用场景
  • 服务端返回:小文件用 Buffer,大文件用 Stream

选择合适的文件传输方案需要综合考虑:文件大小、用户体验、浏览器兼容性、服务器资源等因素。

原文: https://juejin.cn/post/75962309

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1184792.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网站一级域名和二级域名区别有哪些?一定要区分清楚

一、什么是一级域名&#xff1f; 一级域名&#xff0c;通常由后缀和核心主体组成。核心主体是企业、组织或个人注册的唯一标识&#xff0c;后缀则分为通用顶级域名&#xff08;如.com、.org、.net&#xff09;、国家/地区顶级域名&#xff08;如.cn、.uk、.jp&#xff09;两类。…

当红松小课“开”进风景里,银发文旅的“学游融合”新样本

随着老年群体对精神文化生活的追求日益深入&#xff0c;传统的观光旅游已难以满足其深层需求。近年来&#xff0c;一种融合了学习、探索与社交的新型旅行方式——“银发研学游”悄然兴起并备受青睐。在这股浪潮中&#xff0c;作为红松旗下专注服务退休人群的兴趣学习品牌&#…

关于“上瘾”的思考

如何理解上瘾这个事&#xff1f; 比如喝茶&#xff0c;喝咖啡&#xff0c;焚香&#xff0c;打游戏这些事儿。 我觉得他会让我浪费很多时间&#xff0c;长期对自己身体有害。 当“我想要”变成了“我需要”&#xff0c;我究竟是这些仪式的主人&#xff0c;还是它们的囚徒&#x…

钵施然播种机,让农作效率更高!

在农业机械化的大潮中&#xff0c;新疆钵施然凭借其创新的2MBJ系列玉米铺膜播种机&#xff0c;引领了行业的新潮流。这款播种机不仅在功能上全面升级&#xff0c;更在设计上融入了多项专利技术&#xff0c;为现代农业带来了前所未有的便捷与高效。2MBJ系列播种机的最大创新之处…

【必收藏】大模型核心技术全解析:从Transformer架构到训练评估的完整指南

本文系统介绍了大模型的核心技术体系&#xff0c;包括Transformer与MOE两种架构设计&#xff0c;GPT与BERT两种经典模型&#xff0c;详细解析了大模型训练流程中的词表构建、文本向量化、多头注意力机制等关键环节&#xff0c;并阐述了模型评估方法与TensorFlow、LangChain等开…

合同管理系统AI能力接口文档:架构师的规范化编写指南

合同管理系统AI能力接口文档&#xff1a;架构师的规范化编写指南 引言 1.1 痛点引入&#xff1a;为什么需要规范AI能力接口文档&#xff1f; 在合同管理系统的AI化转型中&#xff0c;你是否遇到过以下问题&#xff1f; 对接混乱&#xff1a;前端开发说“不知道AI接口需要什…

2026年失业潮,失业率狂飙18.1%,史上最难就业季即将来临,该如何破局?

前言 【2025年被称为最难就业年&#xff0c;1158万大学生面临难题】 距离2025年毕业季已经过去&#xff0c;毕业学员将面临空前严峻的就业压力&#xff01;具国家统计局的数据显示&#xff0c;1-2月份&#xff0c;16至24岁年轻人的失业率飙到18.1%&#xff0c;也就是说&#…

如何在升级前轻松备份 iPhone(4 种方法)

在升级到新手机之前&#xff0c;你应该备份你的 iPhone 吗&#xff1f;当然&#xff0c;如果你不想丢失重要数据&#xff0c;或者打算将数据恢复到新手机&#xff0c;就应该备份。而且&#xff0c;备份步骤很简单&#xff0c;只要你读完本指南&#xff0c;就能知道如何在升级前…

前端开发提效神器:Open-Lovable 克隆网页 + cpolar 远程访问太香了

Open-Lovable 作为一款开源的前端工具&#xff0c;核心能力是通过 AI 对话快速生成完整的 React 应用&#xff0c;最实用的功能就是网页克隆 —— 输入目标网址、选择合适的 AI 模型&#xff0c;就能自动生成结构清晰的 React 代码&#xff0c;还集成了 E2B 沙箱和 Firecrawl 抓…

收藏!Java程序员转行大模型开发:从入门到落地的全指南

在人工智能&#xff08;AI&#xff09;浪潮席卷全球的当下&#xff0c;传统编程领域的从业者纷纷寻求新的职业突破口&#xff0c;其中Java程序员转向大模型开发&#xff0c;成为了兼具挑战与机遇的热门选择。这不仅是突破职业瓶颈的重要路径&#xff0c;更能借助AI赛道的红利&a…

一篇文章教会你企业SRC捡洞之CORS(教程+实战案例),从零基础到精通,理论与实践结合的最佳路径!

一篇文章教会你企业SRC捡洞之CORS(教程实战案例) 0x01 前言 提到CORS这个漏洞&#xff0c;刚开始可谓是让本安服仔恨的牙痒痒&#xff0c;因为笔者还是个脚本小子的时候&#xff0c;用扫描器总是能扫出一堆的CORS&#xff0c;但是因为当时菜的抠脚&#xff0c;只会拿着扫描器…

爬虫详解:原理、常用库与实战案例!

一.爬虫介绍 1.什么是爬虫 爬虫是门很有意思的技术&#xff0c;可以通过爬虫技术获取一些别人拿不到或者需要付费才能拿到的东西&#xff0c;也可以对大量数据进行自动爬取和保存&#xff0c;减少时间和精力去手动做一些累活。 可以说很多人学编程&#xff0c;不玩点爬虫确实…

如何将文件从 iPhone 传输到 Android

如果您也想与朋友分享文件&#xff0c;或者从 iPhone 切换到 Android&#xff0c;那么您需要这份指南&#xff0c;其中包含 5 种有效的方法。您可以使用其中一种方法&#xff0c;轻松地将文件从 iPhone 发送到 Android。特征/方法第一部分&#xff1a;如何通过“切换到安卓”功…

86万商户装不满高德榜单

膨胀的高德扫街榜&#xff0c;商户还够用吗&#xff1f;作者|景行编辑|古廿9.96亿、6.6亿、86万、2559。这是高德扫街榜上线100天交出的四个数字。1月7日&#xff0c;在高德扫街榜2026发布会上&#xff0c;高德CEO郭宁首次系统披露了这组数据&#xff1a;高德App月活跃用户规模…

重磅!建筑防火阀门新规 GB15930-2024 落地倒计时,2026 年实施!这些核心变化必看

2024年11月28日&#xff0c;国家市场监督管理总局、国家标准化管理委员会正式发布GB15930-2024《建筑通风和排烟系统用防火阀门》国家标准&#xff0c;将于2026年3 月1日全面实施&#xff0c;替代沿用17年的GB15930-2007 版标准。作为建筑消防系统的核心部件&#xff0c;防火阀…

合作品牌|威秀自助KTV:潮玩K歌新据点

才盛云&#xff0c;深耕自助KTV赛道的科技领军者&#xff0c;核心团队携连锁标准化基因&#xff0c;以AISaaS物联网技术打造全流程智慧运营系统&#xff0c;实现30秒扫码开厢、远程管控、动态定价等全链路无人化操作&#xff0c;帮门店降本增效、轻资产运营。我们不仅提供硬核技…

首程控股(0697.HK)机器人投资回报显现:明星项目已获超10倍收益

在人工智能与实体经济深度融合的浪潮中,机器人产业正成为最具颠覆性的前沿阵地。近日,首程控股(0697.HK)以其在机器人赛道亮眼的投资战绩引发市场高度关注。公司不仅宣布将于本周日(2026年1月18日)晚上7:50在抖音及视频号开启直播首秀,以机器人产品销售与市场化验证为核心,推动…

一文读懂贵金属上涨的四大引擎

2025年黄金大约上涨65%&#xff0c;创下数十年来的最佳年度涨幅&#xff1b;白银全年飙升逾150%&#xff0c;一度突破每盎司80美元。2026年初&#xff0c;贵金属的牛市行情依然延续&#xff0c;现货黄金价格在1月中旬一度触及4622美元/盎司&#xff0c;白银更是暴涨并逼近90美元…

【建议收藏】智能体(AGENT)与工作流(WORKFLOW):大模型应用落地的核心逻辑

本文系统介绍了智能体(AGENT)与工作流(WORKFLOW)的区别与融合&#xff0c;阐述了构建高效Agentic系统的核心逻辑。详细分析了七种工作流类型及其适用场景&#xff0c;包括增强型LLM、提示词链接、路由、并行、编排工作者、评估者-优化者和自主智能体工作流&#xff0c;并对N8N、…

自助KTV行业生态领航者

谢毅&#xff0c;中国自助KTV行业开创者与生态构建者&#xff0c;从2020年深耕自助业态起步&#xff0c;至2025年完成全产业链AI赋能生态布局&#xff0c;以五年精准战略推进&#xff0c;实现自助KTV行业从0到1的开创、从1到N的升级&#xff0c;成为推动行业数字化、智能化变革…