Skip to content
文章目录

文件上传组件

需要提前掌握

input file

html
<input type="file" accept="image/gif,image/jpeg,image/jpg,image/png" capture="camera" multiple />

作用

该组件在界面的表现形式是一个上传按钮,点击该组件会打开文件选择弹窗,选择了文件之后,这个上传按钮会显示文件名称

事件

change 事件

通过 change 事件就能获取到用户选择的文件的文件对象

html
<input
  ref="realUploadFileBtnRef"
  type="file"
  class="realUploadFileBtn"
  accept="image/gif,image/jpeg,image/jpg,image/png"
  @change="onGetFile"
/>
ts
function onGetFile(e: Event) {
  // 获取文件上传按钮的dom实例
  const inputDom = e.target as HTMLInputElement
  if (!inputDom) return

  // 获取用户选择的所有文件
  const files = inputDom.files
  if (!files) return

  // 如果未指定 multiple 属性,则一次只能选择一个文件,使用 files[0] 即可获取到,如果指定了 multiple 属性,则需要循环 files 数组
  const imageFile = files[0]
  // 这里可以进行真正的文件类型判断
  console.log(imageFile)
  // 因为 accept 并不能真正意义上的禁止用户选择某类文件,这里获取到的文件类型,也不能完全保证正确,因此前端就做到 accept 控制下即可,严格的文件类型校验,还是留给后端吧
  console.log(imageFile.type)
  // size属性能够获取到文件大小
  console.log(imageFile.size)
  const picInfo: PicInfo = {
    _id: `${new Date().getTime()}-${++seed}`,
    // 这里能够获取到文件名
    name: imageFile.name,
  }
  // 每次change事件最后都要将文件上传按钮的value值设置为空字符,不然,下次再选择同一个文件,将无法再触发change事件
  inputDom.value = ''
  convertImgToBase64(imageFile, (base64: string) => {
    picInfo.url = base64
    picInfo.id = picInfo._id
    val.value = picInfo
    emitTrigger()
  })
}

/**
 * 将图片转成base64格式
 *
 * @param imageFile 图片文件
 * @param callback 转成成功函数回调(这里是接收转换成功结果的函数)
 * @param errorCallback 转成失败函数回调(这里是接收转换失败结果的函数)
 */
function convertImgToBase64(imageFile: File, callback: (base64: string) => void, errorCallback?: (error: any) => void) {
  try {
    let reader = new FileReader()
    reader.onload = function (ev: ProgressEvent<FileReader>) {
      if (callback) {
        const fileReader = ev.target
        if (!fileReader) {
          errorCallback && errorCallback('图片转base64失败')
          return
        }
        let base64Str = fileReader.result as string
        callback(base64Str)
      }
    }
    reader.readAsDataURL(imageFile)
  } catch (error) {
    console.error(error)
    if (errorCallback) {
      errorCallback(error)
    }
  }
}

属性说明

capture

调用设备媒体。在 webapp 上使用 input 的 file 属性,指定 capture 属性可以调用系统默认相机、摄像和录音功能。

capture 表示,可以捕获到系统默认设备的媒体信息,如下:

capture="camera" 相机
capture="camcorder" 摄像机
capture="microphone" 录音
html
<input type="file" accept="image/*" capture="camera" />
<input type="file" accept="video/*" capture="camcorder" />
<input type="file" accept="audio/*" capture="microphone" />
multiple

设置了该属性之后,将支持多文件选择

accept

设置 input file 默认选择的文件类型。当我们打开系统的选择文件弹框的时候,默认界面中呈现的文件类型。通过使用 accept 属性限制文件类型,如果想支持多种类型的话,只要在 accept 里面放置多个属性就可以了(逗号隔开)。

选择 JSON 文件

html
<input id="file1" type="file" accept=".json" />

选择图片文件

html
<input type="file" accept="image/gif,image/jpeg,image/jpg,image/png" />

选择文档文件

html
<input id="file1" type="file" accept=".xls,.doc,.txt,.pdf" />

WARNING

accept 值支持通配符,如:<input id="file1" type="file" accept="image/*"/>, 但这有可能造成Webkit内核下打开文件弹窗缓慢的问题,因此尽量不要使用这种形式的通配符,而是要指定所有的类型.像这样:<input type="file" accept="image/gif,image/jpeg,image/jpg,image/png">

accept 属性并不会验证选中文件的类型,只是为开发者提供了一种引导用户做出期望行为的方式,用户还是有办法绕过浏览器的限制。 因此,在服务器端进行文件类型验证还是很有必要的。

axios 实现文件上传

单文件上传

js
async handleUploadFile(event) {
  const file = event.target.files[0]
  let formData = new FormData()
  formData.append('files', file)
  formData.append('test', '其他参数')
  const res = await service({
    url: '/api/files/upload',
    method: 'POST',
    headers: {
       'Content-Type': 'multipart/form-data'
    },
    data: formData
  })
  console.log(res.data);
}

多文件上传

html
<input id="upload_file" type="file" multiple @change="v_upload_files" />
js
v_upload_files(e) {
  let files = e.target.files
  let formData = new FormData()
  // formData重复的往一个值添加数据并不会被覆盖掉,可以全部接收到,可以通过formData.getAll('files')来查看所有插入的数据
  for (let i = 0;i < files.length;i++) {
    formData.append('files', files[i])
  }
  formData.append('test','其他参数')
  let url = '/files/uploadfiles'
  let headers = {
    'Content-Type': 'multipart/form-data'
  }
  axios.post(url, formData, {headers: headers})
}

上传的文件类型校验

accept 并不能完全禁止用户上传的实际文件类型,可以在文件选择框中,选择*/*即可绕过 accept 的限制,还可以将其他类型的文件的后缀改为 accept 支持的类型,所以单纯的 accept 属性,是没法完全限制住的. change 事件中可以获取到 file 对象,file 对象有 type 属性,这里也能拿到文件类型,但也无法完全保证准确。还可以通过二进制判断,但对前端而言,检查二进制就有点复杂了,同时检查二进制也有很多坑。因此个人认为前端上传文件类型检查,限制下 accept,检查下文件名的后缀即可。不必做复杂的校验.

如下文章,有讲解 JS 通过文件的二进制内容判断文件类型

JavaScript 如何检测文件的类型?

上传文件格式验证如何实现?

前端——利用 File signature 精准校验文件类型

Web 前端 Js 文件上传类型限制(根据文件头信息判断)

如果一定要通过二进制检查,来进行文件类型判断,可以考虑使用第三方组件来实现检查

file-type

参考资料

HTML5 - 限制 input file 可选择的文件类型

input type="file"属性详解

H5 利用 JS 实现拍照效果

前端将图片直接转成 Base64,纯前端操作

axios 实现 文件上传

vue 使用 Axios 上传文件(post 请求)

axios 上传文件

使用 axios 发送 post 请求上传文件(multipart/form-data)到后端

Vue3 + TS + axios 文件切片上传 Hook

vue+axios+FormData 上传多个文件

axios 同时上传多个文件