文件上传原理
浏览器把文件塞进HTTP请求体里,文件这事比大多数人想的上传要粗糙。你选一个2GB的原理电影,点上传,文件浏览器不会一口气读进内存——它读一小块,上传发一小块,原理像用勺子往邮筒里喂信。文件这个"小块"的上传大小由浏览器决定,Chrome通常按64KB切,原理但具体值它从不告诉你。文件 表单上传是上传最老的玩法。 这种方式的毛病很明显。页面会刷新,用户体验断档。更麻烦的是,你没法知道上传进度——浏览器把整个请求交出去之后,就只剩等待。早年有些网站用隐藏iframe模拟异步,把form的target指向一个iframe,这样页面不跳,但进度条依然是假的,靠定时器猜。 XMLHttpRequest Level 2改了规则。它允许你直接发一个File对象或Blob,不用form包裹。代码大概长这样: 关键在 fetch API后来也支持上传,但有个硬伤——它没有原生进度事件。你想监控进度,得用ReadableStream自己包一层,把文件切片,逐片发,每发完一片手动更新进度。麻烦,但可控。我见过有人用fetch加TransformStream做上传,代码量是XHR的三倍,好处是能中途暂停和恢复。 分片上传是另一个维度的东西。文件切成固定大小的块,比如5MB一片,每片单独发一个请求。服务端收到后按序号拼回去。这解决三个问题:大文件超时、断点续传、并发加速。但代价是服务端得维护一个临时状态,记录哪些片到了、哪些没到。S3的分片上传接口要求你先调CreateMultipartUpload拿一个UploadId,然后每片带上PartNumber,最后CompleteMultipartUpload时提交所有ETag。少一个ETag,整个文件作废。 有个细节容易被忽略:浏览器发文件时,Content-Type通常由文件扩展名推断。你传一个没有扩展名的文件,Content-Type就是application/octet-stream。服务端如果只认image/jpeg,这文件就被拒了。更隐蔽的是,有些后端框架会自动解析multipart,把文件写到临时目录,你还没开始处理,磁盘已经占了一块。临时目录满了,上传就挂,报错还特别含糊。 我自己的经验是,小于10MB的文件,直接XHR发就完了,别折腾。大于100MB,必须分片,而且分片大小别超过10MB,否则移动网络下重传成本太高。至于进度条,如果服务端做了CDN回源,你看到的100%可能只是边缘节点收完了,源站还在慢慢吞。这时候刷新页面,文件可能还没落盘。<form method="post" enctype="multipart/form-data">,原理关键就在这个enctype。文件不写它,上传浏览器只发文件名,原理文件内容根本不过去。写了之后,请求体变成一段一段的,每段用boundary分隔。boundary是随机字符串,长得像----WebKitFormBoundary7MA4YWxkTrZu0gW。服务端拿到body,靠这个字符串切出每个字段和文件。我抓包看过,一个10MB的图片,请求头里Content-Length是10486213,body里前几百字节全是boundary和Content-Disposition描述,真正的二进制数据跟在后面。const xhr = new XMLHttpRequest(); xhr.upload.onprogress = (e) => { console.log(e.loaded / e.total); }; xhr.open('POST', '/upload'); xhr.send(fileInput.files[0]);xhr.upload这个对象。它专门派发上传相关事件,progress事件里loaded和total都是真实字节数。这是第一次,前端能拿到诚实的进度。不过有个坑:如果服务端开了gzip压缩请求体,total可能不准,因为浏览器算的是压缩前的长度。
-
上一篇
-
下一篇
- 最近发表
- 随机阅读
- 搜索
-
- 友情链接
-