文件上传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. 大文件上传

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

相关推荐
喵叔哟3 分钟前
重构代码之取消临时字段
java·前端·重构
还是大剑师兰特43 分钟前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts
王解44 分钟前
【深度解析】CSS工程化全攻略(1)
前端·css
一只小白菜~1 小时前
web浏览器环境下使用window.open()打开PDF文件不是预览,而是下载文件?
前端·javascript·pdf·windowopen预览pdf
方才coding1 小时前
1小时构建Vue3知识体系之vue的生命周期函数
前端·javascript·vue.js
阿征学IT1 小时前
vue过滤器初步使用
前端·javascript·vue.js
王哲晓1 小时前
第四十五章 Vue之Vuex模块化创建(module)
前端·javascript·vue.js
丶21361 小时前
【WEB】深入理解 CORS(跨域资源共享):原理、配置与常见问题
前端·架构·web
发现你走远了1 小时前
『VUE』25. 组件事件与v-model(详细图文注释)
前端·javascript·vue.js
Mr.咕咕1 小时前
Django 搭建数据管理web——商品管理
前端·python·django