2026/4/16 13:19:55
网站建设
项目流程
家具网站建设策划,网站建设哪些是需要外援的问题,织梦 和wordpress,外包软件公司在哪里去接项目本文介绍浏览器与服务器之间文件传输的常见方式#xff0c;涵盖文件的获取、上传、下载全流程#xff0c;并附带代码示例。 1 浏览器获取用户本地文件 在浏览器中根据不同场景#xff0c;有多种获取文件的方式。 1.1 点击上传 通过点击文件表单实现上传#xff0c;最基础、…本文介绍浏览器与服务器之间文件传输的常见方式涵盖文件的获取、上传、下载全流程并附带代码示例。1 浏览器获取用户本地文件在浏览器中根据不同场景有多种获取文件的方式。1.1 点击上传通过点击文件表单实现上传最基础、兼容性最好的方式。input typefile idfile / script const file document.getElementById(file) file.addEventListener(change, (e) { const file e.target.files[0] console.log( ~ file --, file) }) /scriptinput typefile iddir webkitdirectory multiple / script const input document.getElementById(dir); input.addEventListener(change, (e) { const files e.target.files; console.log( ~ files --, files) }); /script1.2 拖拽上传直接拖动文件到页面交互体验好符合直觉适合批量文件。div iddrop stylewidth: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) }); /script1.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) } } }); /script1.4 通过媒体设备获取资源适用于需要捕获摄像头或麦克风媒体资源时的特殊场景。button idbtnOpen打开摄像头/button button idbtnShot截图/button video idvideo autoplay playsinline muted/video img idpreview 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 } )) }); /script1.5 File System Access API非标准的实验性接口功能强大支持读写。button idopenFile打开文件/button button idopenFiles打开多个文件/button button idopenDir打开目录/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) }); /script2 浏览器发送文件拿到File之后可以使用fetch发送如果需要显示进度可以使用XMLHttpRequest。请求体通常使用multipart/form-data编码格式或者直接上传二进制文件数据。Content-Type: multipart/form-dataContent-Type: application/octet-streamContent-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; namebiz avatar ------WebKitFormBoundaryA1B2C3D4 Content-Disposition: form-data; namefile; filenameavatar.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.jpg3 浏览器下载文件下载文件主要有两大类下载服务端资源或前端生成/操作文件数据。3.1 基于服务端资源服务端提供文件下载链接Content-Type: image/jpeg Content-Disposition: attachment; filenameavatar.jpg Transfer-Encoding: chunked前端可以基于静态a标签动态创建a标签或者通过导航window.location/window.open触发下载a href/api/file-stream下载/a button iddownload-stream按钮下载/button button idnav-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 downloadavatar.jpg下载/a如果服务端有通过Content-Disposition指定文件名浏览器通过download属性配置的文件名无效。浏览器导航文件链接大致过程解析响应头根据Content-Type判断 MIME 类型决定浏览器如何处理文件根据Content-Disposition决定显示还是下载inline→ 默认值在页面中显示文件内容attachment→ 下载文件下载文件时会根据Content-Length决定是否显示进度条接收响应体并写入内存或文件3.2 基于二进制数据适用于前端生成文件或者导出 canvas 等场景。button iddownload下载文本/button button iddownloadCanvas下载 canvas 图片/button canvas idcanvas width200 height200 styleborder: 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) }) /script3.3 基于 data URL适用于极小文本数据特定场景有用。button iddownload下载 SVG/button script const svg svg xmlnshttp://www.w3.org/2000/svg width200 height200 rect fill#4a90d9 width200 height200/ text x100 y110 text-anchormiddle fill#fff font-size24Hello SVG/text /svg document.getElementById(download).addEventListener(click, () { const dataUrl data:image/svgxml;charsetutf-8, encodeURIComponent(svg) const a document.createElement(a) a.href dataUrl a.download hello.svg a.click() }) /script3.4 File System Access API非标准的实验性接口直接保存到指定位置。button iddownload下载文本/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() }) /script4 服务端返回文件NodeJS一般可以通过Buffer或Stream的方式返回文件。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