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

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

相关推荐
醉城夜风~1 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华2 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子4 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅4 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni4 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学6 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端
赵庆明老师6 小时前
Vben精讲:21-详解web-antd:tsconfig.json
前端·json·vim
wc886 小时前
微软EDGE浏览器功能学习
前端·学习·edge
Csvn7 小时前
🎯 原生 `<dialog>` 元素:终于可以扔掉一半的自定义弹窗组件了?
前端