文件上传upload的一些总结

背景:随着业务的复杂性提高,前端上传文件变得越来越常见,那么深入了解上传这一行为很有必要;

一、input标签 type="file"

1.1 基本用法:

html 复制代码
<input type="file" />

思考🤔,我们是否可以使用 v-model来获取选择的文件呢,hhh, 答案是不行;(注意下面的写法是不允许的)

html 复制代码
// v-model cannot be used on file inputs since they are read-only. Use a v-on:change listener instead.vue(59)
 <input v-model="filePath" type="file" />

在Vue中,使用v-model指令可以将表单输入和应用程序状态进行双向绑定。然而,<input type="file" />元素是一个特殊情况,因为它的值是只读的,无法直接通过v-model来绑定。如果你想要获取文件路径,你可以使用@change事件监听器来捕获文件选择的变化,然后在事件处理程序中更新Vue实例的数据。

js 复制代码
<input type="file" @change="handleFileChange" />

const handleFileChange = (event) => {
  console.log(event.target?.files); // FileList {0: File, length: 1}
  console.log(event.target?.value); // 'C:\\fakepath\\test.pdf'
};

备注: 为了阻止恶意软件猜测文件路径,该值的字符串表示总是以 C:\fakepath 为前缀的文件名,而并不是文件的真实路径

1.2 其他参数

accept

定义input能选择的文件类型,例如:accept=".pdf,.zip,.ofd", 属性接受的是一个字符串(唯一文件类型说明符号)可以是 1、一个以英文句号(".")开头的合法的不区分大小写的文件名扩展名。例如:.jpg、.pdf 或 .doc; 2、一个不带扩展名的 MIME 类型字符串

上表格转自:input type="file" accept=".zip"上传文件响应慢的问题解决办法

multiple

布尔类型,用于表示是否多选

...

1.3 上传文件夹

input 中还存在一个不标准的属性: webkitdirectory ,表示存在兼容性,不过大部分都兼容

js 复制代码
<input
  ref="uploadDirRef"
  type="file"
  id="file"
  accept=".pdf,.ofd"
  :webkitdirectory="true"
  :mozdirectory="true"
  :odirectory="true"
  @change="handleDirChange"
/>

const handleDirChange = (event) => {
  console.log(event.target?.files); // FileList {0: File, length: 1}
};

在选择文件目录后,该目录及其整个内容层次结构将包含在所选项目集内。可以使用 webkitEntries (en-US) 属性获取选定的文件系统条目,意思是:文件夹中还有文件夹的多级结构,也能成功选到其中的文件;

使用这种方式存在的问题:

  1. 会显示浏览器的上传提示弹窗,(比较丑)
  2. 无法判断空文件夹,上传了空文件夹不会触发change事件,所以响应不好

二、upload组件

基础用法

html 复制代码
<template>
  <el-upload
    v-model:file-list="fileList"
    class="upload-demo"
    action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
    multiple
    :on-preview="handlePreview"
    :on-remove="handleRemove"
    :before-remove="beforeRemove"
    :limit="3"
    :on-exceed="handleExceed"
  >
    <el-button type="primary">Click to upload</el-button>
    <template #tip>
      <div class="el-upload__tip">
        jpg/png files with a size less than 500KB.
      </div>
    </template>
  </el-upload>
</template>

拖拽上传

upload组件里面支持拖拽上传,源码 封装了一个 upload-dragger 子组件,主要利用了DragEvent.dataTransfer 这个只读属性;在进行拖放操作时,传输的数据。核心代码如下:

js 复制代码
<template>
  <div
    :class="[ns.b('dragger'), ns.is('dragover', dragover)]"
    @drop.prevent="onDrop"
    @dragover.prevent="onDragover"
    @dragleave.prevent="dragover = false"
  >
    <slot />
  </div>
</template>

const onDrop = (e: DragEvent) => {
  if (disabled.value) return
  dragover.value = false

  e.stopPropagation()

  const files = Array.from(e.dataTransfer!.files)
  emit('file', files)
}

源码解析

看了一下源码,主要核心代码还是使用的 input标签 type="file",主要加上了一些其他的样式,还有文件列表;

html 复制代码
<input
  ref="inputRef"
  :class="ns.e('input')"
  :name="name"
  :multiple="multiple"
  :accept="accept"
  type="file"
  @change="handleChange"
  @click.stop
/>

三、useFileDialog

在vueUse库中,存在一个处理文件上传的 hooks: useFileDialog

js 复制代码
<script setup lang="ts">
import { useFileDialog } from "@vueuse/core";

const { files, open, reset, onChange } = useFileDialog({
  accept: "image/*", // 设置类型
  directory: true, // 设置 - 选择文件夹
});

onChange((files) => {
  /** do something with files */
});
</script>

<template>
  <div>
    <button type="button" @click="() => open()">Choose file</button>
  </div>
</template>

查看源码发现与 核心就是 input标签 type="file",与前面第一条很类似,不过封装思路值得学习;

四、js新api,window.showOpenFilePicker()

js新增了 window.showOpenFilePicker()、 window.showDirectoryPicker()、window.showSaveFilePicker() 等api,兼容性查,慎用;

五、其他细节

1. 中断终止

因为上传是个耗时的任务,用户很可能中途停止,如关闭弹窗、关闭抽屉等,这时候我们需要终止上传;

使用action方式的中断方法,调用组件暴露给外部的api

js 复制代码
uploadRef.value?.abort();

使用:http-request=自定义上传请求的,可以使用AbortController中断

js 复制代码
const controller = new AbortController()

try { 
    const r = await fetch('/json', { signal: controller.signal }); 
} catch (e) {}


// 取消上传
controller.abort()

参考:AbortController的使用

2. 大文件上传

一般大文件上传需要切片 还要考虑断点重传,笔者还未研究这块,暂时给自己留个作业

相关推荐
卸任4 小时前
Vibe coding工具使用心得
前端·ai编程
brien95 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance5 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M5 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写5 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李5 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易6 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马6 小时前
前沿视界
前端
kyriewen6 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易6 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员