前端文件上传

本文已整理到 Github,地址 👉 blog

如果我的内容帮助到了您,欢迎点个 Star 🎉🎉🎉 鼓励鼓励 :) ~~

我希望我的内容可以帮助你。现在我专注于前端领域,但我也将分享我在有限的时间内看到和感受到的东西。


简单文件上传

文件上传的传统形式,是使用表单元素 file

<input type="file" id="file-uploader">

你可以添加 change 事件监听器读取 event.target.files 文件对象。

const fileUploader = document.getElementById('file-uploader')
fileUploader.addEventListener('change', (e) => {
  const files = e.target.files
  console.log('files', files)
})

多个文件上传

使用 multiple 属性

<input type="file" id="file-uploader" multiple />

文件元数据

在成功上传文件内容后,您可能需要显示该文件内容。对于图片,如果我们在上传后不立即将上传的图片显示给用户,则会感到困惑。

每当上传文件时,File 对象都会包含元数据信息,如文件名称、大小、上次更新时间、类型等。此信息可用于进一步验证和决策。

const fileUploader = document.getElementById('file-uploader')

// 侦听更改事件并读取元数据
fileUploader.addEventListener('change', (e) => {
  // 获取文件列表数组
  const files = e.target.files

  // 循环浏览文件并获取元数据
  for (const file of files) {
    const name = file.name
    const type = file.type ? file.type: 'NA'
    const size = file.size
    const lastModified = file.lastModified
    console.log({ file, name, type, size, lastModified })
  }
})

上传前预览图像

我们准备一个上传文件控件,并为预览所选文件准备 img 元素,结构如下:

<input type="file" id="fileInput" />

<img id="preview" />

getElementById() 方法可以获取这两个元素:

const fileEle = document.getElementById('fileInput')
const previewEle = document.getElementById('preview')

使用 URL.createObjectURL() 方法

URL.createObjectURL() 方法包含一个表示参数中给出的对象的 URL。这个新的 URL 对象表示指定的 File 对象或 Blob 对象。

fileEle.addEventListener('change', function (e) {
  // 获取所选文件
  const file = e.target.files[0]

  // 创建引用该文件的新 URL
  const url = URL.createObjectURL(file)

  // 设置预览元素的源
  previewEle.src = url
})

使用 FileReader 的 readAsDataURL() 方法

  • 使用 FileReader 对象将文件转换为二进制字符串。然后添加 load 事件侦听器,以获得成功文件上传的二进制字符串。
  • FileReader.readAsDataURL() 方法用于读取指定的 BlobFile对象。
// 获取 FileReader 的实例
const reader = new FileReader()

fileUploader.addEventListener('change', (e) => {
  const files = e.target.files
  const file = files[0]

  // 上传后获取文件对象,以 URL 二进制字符串的形式读取数据
  reader.readAsDataURL(file)

  // 加载后,对字符串进行处理
  reader.addEventListener('load', (e) => {
    // 设置预览元素的源
    previewEle.src = reader.result
  })
})

accept 属性

使用 accept 属性来限制要上传的文件类型。

<input type="file" id="file-uploader" accept=".jpg, .png" multiple>

上面示例中,浏览器将只允许具有 .jpg.png 的文件类型。

验证文件大小

我们读取了文件的大小元数据,可以使用它进行文件大小验证。您可以允许用户上传高达 1MB 的图像文件。

// 文件上载更改事件的侦听器
fileUploader.addEventListener('change', (event) => {
  // 读取文件大小
  const file = event.target.files[0]
  const size = file.size

  let msg = ''

 // 检查文件大小是否大于 1MB,提示对应消息。
  if (size > 1024 * 1024) {
    msg = `<span style="color: red;">允许的文件大小为 1MB。您尝试上载的文件属于${returnFileSize(size)}</span>`
  } else {
    msg = `<span style="color: green;"> ${returnFileSize(size)} 文件已成功上载。 </span>`
  }

  // 向用户显示消息
  feedback.innerHTML = msg
})

显示文件上传进度

更好的可用性是让用户了解文件上传进度。XMLHttpRequest 第二版还定义了一个 progress 事件,可以用来制作进度条。

先在页面中放置一个 progress 标签

<label id="progress-label" for="progress"></label>
<progress id="progress" value="0" max="100" value="0">0</progress>

定义 progress 事件的回调函数

const reader = new FileReader()

reader.addEventListener('progress', (e) => {
  if (e.loaded && e.total) {
    // 计算完成百分比
    const percent = (e.loaded / e.total) * 100
    // 将值设置为进度组件
    progress.value = percent
  }
})

上传目录

有一个非标准属性 webkitdirectory,使我们能够上传整个目录。

虽然最初仅针对基于 WebKit 的浏览器实施,但 WebkitDirectory 在微软 Edge 以及 Firefox 50 及以后也可用。然而,即使它有相对广泛的支持,它仍然不是标准的,不应该使用,除非你别无选择。

<input type="file" id="file-uploader" webkitdirectory />

拖放上传

主要的 JS 如下:

const dropZone = document.getElementById('drop-zone')
const content = document.getElementById('content')

dropZone.addEventListener('dragover', event => {
  event.stopPropagation()
  event.preventDefault()
  event.dataTransfer.dropEffect = 'copy'
})
dropZone.addEventListener('drop', event => {
  // 获取文件
  const files = event.dataTransfer.files
  // ..
})

查看效果

用对象处理文件

使用 URL.createObjectURL() 方法从文件创建一个唯一的 URL。使用 URL.revokeObjectURL() 方法释放它。

DOM 和 URL.createObjectURL()URL.revokeObjectURL() 方法允许您创建简单的 URL 字符串,可用于引用任何可以使用 DOM 文件对象引用的数据,包括用户计算机上的本地文件。

示例:

<div>
  <h1>使用 Object URL</h1>
  <input type="file" id="file-uploader" accept=".jpg, .jpeg, .png" >
  <div id="image-grid"></div>
</div>
const fileUploader = document.getElementById('file-uploader')
const reader = new FileReader()
const imageGrid = document.getElementById('image-grid')

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

推荐阅读更多精彩内容