浏览器文件上传浅淡

form 表单的各种格式

在出现 ajax 之前,前端是使用 form 表单进行提交数据的,它的结构大概是这样:

<form>
  <input type="text" name="username"/>
  <button type="submit">提交</button>
</form>

在 form 表单进行提交时,则支持有四种方式(来自:https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Submitting_forms_and_uploading_files):
application/x-www-form-urlencoded, text/plain, multipart/form-data, GET 方式即 URL 传参。

  1. GET 类请求

其内容是添加到 URL 的 query 部分的:

foo=bar&baz=The+first+line.%0D%0AThe+second+line.%0D%0A
  1. application/x-www-form-urlencoded

POST 方式进行传输与 GET 类似内容时,其数据可放在 body 内,application/x-www-form-urlencoded
方式内保证数据格式与普通的 GET 方式类似:

Content-Type: application/x-www-form-urlencoded

foo=bar&baz=The+first+line.%0D%0AThe+second+line.%0D%0A
  1. multipart/form-data
    这种方式是支持传输其他类型的数据,比如文件或者 blob 类型数据。
    其格式如下:
------WebKitFormBoundaryKnsKCAS4s0kSeNQ3
Content-Disposition: form-data; name="外卖测试.png"; filename="Pictures/aa.png"
Content-Type: image/png


------WebKitFormBoundaryKnsKCAS4s0kSeNQ3
Content-Disposition: form-data; name=".localized"; filename="Pictures/.localized"
Content-Type: application/octet-stream


------WebKitFormBoundaryKnsKCAS4s0kSeNQ3
Content-Disposition: form-data; name="start.png"; filename="Pictures/start.png"
Content-Type: image/png


------WebKitFormBoundaryKnsKCAS4s0kSeNQ3--

它的主要结构是以分界符进行分隔,每个分界符后面带有 \r\n,最后一个分界符会多个 --,表示数据传输结束。
它在服务端的解析可以参考 https://blog.csdn.net/shmnh/article/details/49012417

input[type=file] 的一些属性

<input type="file"/>

和其他类型的 input 相同,事件都是比较类似的。获取值的方式为添加 change 事件即可。
除此之外,还支持几个常用的属性

  • multiple 多选属性,可进行多选
  • webkitdirectory 选择文件夹功能,注意该属性只支持 chrome 和 firefox
  • accept 设置选择文件的类型,比如 'image/*', 支持图片类型等

在 change 事件中,可以从 e.target.files 获取,它是个伪数组,可用 slice 转换为真正的数组。
内部结构大概如下:

lastModified: 1519638407937,
lastModifiedDate : Mon Feb 26 2018 17:46:47 GMT+0800 (CST) 
name: "aaa.png"
size: 1222046
type: "image/png"
webkitRelativePath: "Pictures/外卖测试.png"

这些参数都是比较有用的参数,利用它们可以实现各种复杂的功能。

实现文件上传

iframe + form

普通 form 实现其中有最大的问题是表单提交后,页面会进行跳转,为了解决这个问题,将 form 的 target 设置为一个隐藏的 iframe,
保证调用完成 submit 后仍旧可停留当前页面

<form action="/api/upload" target="empty-iframe" enctype="multipart/form-data" method="POST">
  <input name="file" type="file" multiple webkitdirectory="" />
  <button type="submit">提交</button>
</form>

<iframe src="" name="empty-iframe" frameborder="0" style="width: 100%;min-height: 400px;"></iframe>

FormData

由于 IE10 后支持了 FormData 对象,这样我们提交数据就很方便了。FormData 使用方法是:

const xhr = new XMLHttpRequest()
const form = new FormData()
form.append(key, data)
form.append(file.name, file.file)
xhr.open('post', action, true)
xhr.send(form)

实现起来就很简单了

图片本地展示

在有些情况下,可能在上传要前展示本地的图片,所以我们需要读取文件。通用的一种方法是使用 URL.createObjectURL,
当然,该方法还是处于草案阶段,但浏览器已经基本支持了。它会生成了一个以 blob:// 开头的地址,指向一个浏览器创建的临时地址。
这样相当于直接网络读取。

对于一些小的图片也可以先进行读取后展示,比如我们知道图片是支持 base64 的方式的,那么我们就可以将图片转为 base64 方式进行读取。
如下例使用 FileReader 进行读取为 base64, 并进行 md5 校验:

const reader = new FileReader()
reader.onload = function (e) {
  dataPicList.querySelector('#id-' + uid).src = e.target.result
  const spark = new SparkMD5()
  spark.append(e.target.result)
  const hexHash = spark.end()
  console.log(hexHash)
}
reader.readAsDataURL(file)

当然,对 base64 获取的 md5 会与二进制的不同,同时对于大文件读取至内存再处理会有诸多问题,这些都是可以考虑的一个方向。

本文中实践的示例在:https://github.com/wenlonghuo/code-test/tree/master/005_upload

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • 前端无法像原生APP一样直接操作本地文件,否则的话打开个网页就能把用户电脑上的文件偷光了,所以需要通过用户触发,用...
    雷波_viho阅读 818评论 0 1
  • 本文包括:1、文件上传概述2、利用 Commons-fileupload 组件实现文件上传3、核心API——Dis...
    廖少少阅读 12,513评论 5 91
  • 如果不考虑ie9兼容性,实现【上传图片】大致的思路如下: 由于公司是将所有上传的图片都放到【代理服务器】里。所以【...
    kakaguo阅读 5,956评论 1 4
  • 公司:宁波大发化纤有限公司 姓名:冯玉停 期数:六项精进224期感谢二组学员,234期感谢三组志工,260期感谢一...
    尘埃wyzh阅读 135评论 0 0
  • 小时候的我们 总是想要爬上山顶 看看山的那一面是不是大海 后来的我们 终于爬上了山顶 发现山的那一面还是山
    2017枫叶阅读 303评论 0 2