前端如何直接上传文件夹

news2024/11/20 10:24:40

前面写了一篇仿写el-upload组件,彻底搞懂文件上传,实现了选择/拖拽文件上传,我们经常看到一些网站支持直接选择整个文件夹上传,例如:宝塔面板、cloudflare托管、对象存储网站等等需要模拟文件路径存储文件的场景。那是怎么实现的呢?

依然从两方面来说:

  1. input选择文件夹
  2. 拖拽文件夹

input选择文件夹

在props.js中加一个属性,upload-folder是否支持上传文件夹

export default {
  // 前面的省略了...
  // 是否支持选择文件夹
  'upload-folder': {
    type: Boolean,
    default: false
  }
}

改一下input标签,依然是根据props的值动态判断是否支持上传文件夹。主要是webkitdirectory这个属性,由于不是一个标准属性,需要加浏览器前缀。

<input 
  type="file" 
  id="file" 
  :multiple="multiple"
  :accept="accept"
  :webkitdirectory="uploadFolder"
  :mozdirectory="uploadFolder"
  :odirectory="uploadFolder"
  @change="handleChange"
>

注意:支持选择文件夹时就只能选择文件夹,无法选择文件。

那么如何获取选择的文件夹呢?其实我们最终要上传的依然是文件,也就是file对象,文件夹也是一个特殊的文件。

依然是通过inputonchange事件回调拿到上传的event

或者直接获取input这个dom对象,然后拿到files属性,结果是一样的。

// input选择文件回调
const handleChange = (event) => {
  console.log('[ files ] >', event.target.files)
  const inputDom = document.querySelector('#file')
  console.log('[ files ] >', inputDom.files)
}

可以看到,比选择单个文件时,多了一个webkitRelativePath属性,并且它是递归选择的文件夹,拿到这个文件夹及其子文件夹下所有的文件,我们可以通过这个属性拿到上传时文件所在的文件夹名称路径

拖拽文件夹

上篇文章讲过拖拽如何拿到文件,首先要准备一个用于拖拽放置的区域。
调用upload组件时,传入drag=true

<div 
  class="drag-box" 
  @dragover="handleDragOver"
  @dragleave="handleDragLeave"
  @drop="handleDrop"
>
  将文件拖到此处,或<span>点击上传</span>
</div>
// 拖放进入目标区域
const handleDragOver = (event) => {
  event.preventDefault()
}
// 拖拽放置
const handleDrop = (event) => {
  event.preventDefault()
  console.log('[ event ] >', event)
}

注意:和input上传不同,拖拽时,是可以同时拖拽文件和文件夹的。

因为可以同时拖拽文件和文件夹,我们就不能直接使用event.dataTransfer.files,如果刚好拖拽进来的是一个文件,那可以这么获取,如果是个文件夹呢?那就不行了。

同时拖拽一个文件和一个文件夹

这时候就要用到event.dataTransfer.items

// 拖拽放置
const handleDrop = (event) => {
  event.preventDefault()
  console.log(event.dataTransfer.items)
}

打印一下看看:

得到一个List类型的数据,里面是两个DataTransferItem,控制台无法直接查看它到底是个什么玩意儿。

看MDN,也看不出它具体是个啥。既然是List,遍历一下看看:

const handleDrop = (event) => {
  event.preventDefault()
  console.log(event.dataTransfer.items)
  for (const item of event.dataTransfer.items) {
    console.log('[ item ] >', item)
  }
}


可以看到不管是文件还是文件夹,都被识别成了file,只不过图片是直接能识别出type为image/png

查看MDN,https://developer.mozilla.org/zh-CN/docs/Web/API/DataTransferItem

点击查看itemPrototype,发现里面有个webkitGetAsEntry方法,执行它就能拿到item的具体信息。

看方法名,带了个webkit,但是这个方法除了Android Firefox浏览器以外都可以用。

for (const item of event.dataTransfer.items) {
  const entry = item.webkitGetAsEntry()
  console.log(entry)
}

依然拖动上面那个图片文件和一个文件夹:


可以看出,文件夹里面还有文件和文件夹,但是只显示了一个文件和一个文件夹,看来拖拽和input上传不一样,它不会自动的把里面所有的文件递归列出来。

通过isDirectory属性,就能区分是文件还是文件夹。除了这些基础属性以外,继续查看Prototype,可以看到还有一系列方法:

先看怎么拿到文件

entry是一个文件时,它有两个方法:createWriter()file(),查看MDN,https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileEntry/createWriter

createWriter()已经废弃了,而且也不是我们今天要用的。

file()才是我们要找的。


这不就是我们熟悉的file对象吗,跟input上传拿到的一毛一样。

再看怎么拿到文件夹

查看MDN的Drop API webkitGetAsEntry()方法,https://developer.mozilla.org/zh-CN/docs/Web/API/DataTransferItem/webkitGetAsEntry可得,如果是文件夹,可以通过createReader方法创建一个文件目录阅读器,然后通过readEntries方法,重新拿到每个item,这就是event.dataTransfer.items里面的每个item

我们写一下试试

依然是之前那个图片和文件夹

只打印出了跟目录下一级的一个文件和一个文件夹,那下面还有一个文件怎么办呢?递归呀!

写一个递归读文件的方法。

const readFiles = async (item) => {
  if (item.isDirectory) {
    // 是一个文件夹
    console.log('=======文件夹=======');
    const directoryReader = item.createReader();
    // readEntries是一个异步方法
    const entries = await new Promise((resolve, reject) => {
      directoryReader.readEntries(resolve, reject);
    });

    let files = [];
    for (const entry of entries) {
      const resultFiles = await readFiles(entry);
      files = files.concat(resultFiles);
    }
    return files;
  } else {
    // 是一个文件
    console.log('=======文件=======');
    // file也是一个异步方法
    const file = await new Promise((resolve, reject) => {
      item.file(resolve, reject);
    });
    console.log('[ file ] >', file);
    return [file];
  }
}

handleDrop方法也要改一下

// 拖拽放置
const handleDrop = async (event) => {
  event.preventDefault()
  console.log(event.dataTransfer.items)

  const files = [];
  const promises = [];
  for (const item of event.dataTransfer.items) {
    const entry = item.webkitGetAsEntry();
    console.log('[ entry ] >', entry);
    promises.push(readFiles(entry));
  }

  const resultFilesArrays = await Promise.all(promises);
  const allFiles = resultFilesArrays.flat();

  console.log('[ All files ] >', allFiles);
}

再次拖拽上传看看

三个文件我们都拿到了。

总结

上传文件夹,还是直接使用input比较简单,使用它能直接拿到文件夹下所有的文件,以及每个文件在本地的路径,代码量也少很多。

拖拽的好处是文件和文件夹能一起上传。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1120211.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

每日刷题|贪心算法初识

食用指南&#xff1a;本文为作者刷题中认为有必要记录的题目 推荐专栏&#xff1a;每日刷题 ♈️今日夜电波&#xff1a;悬溺—葛东琪 0:34 ━━━━━━️&#x1f49f;──────── 3:17 &#x1f…

递福巴士是不是骗局呢?

递福巴士的背景介绍 递福巴士是社区服务机构软件。递福巴士是一家提供公益服务的平台&#xff0c;为社区居民提供各种服务和支持的软件。多年来&#xff0c;递福巴士一直致力于社区服务和社会公益&#xff0c;积极推动社区的发展&#xff0c;改善社区居民的生活质量。 递福巴士…

震坤行、西域和京东工业三大工业电商平台API接口详解和说明

一、震坤行 震坤行是中国领先的B2B电子商务平台之一&#xff0c;主要面向全国的制造商、供应商和采购商&#xff0c;提供物流、供应链等。万邦科技联手震坤行&#xff0c;全面拓展电商业务。电商数据API接口平台新增震坤行接口&#xff0c;可帮助客户轻松查询震坤行网站上的商…

Leetcode—34.在排序数组中查找元素的第一个和最后一个位置【中等】

2023每日刷题&#xff08;六&#xff09; Leetcode—34.在排序数组中查找元素的第一个和最后一个位置 实现代码 /*** Note: The returned array must be malloced, assume caller calls free().*/ int lower_bound(int *arr, int numsSize, int target) {// 左闭右开区间[lef…

Windows 下载编译chromium源码

前言 本文介绍如何下载并编译chromium源码。相关前置条件可参考官方文档。 环境 &#xff1a; Windows 11VS 2022 环境设置 打开cmd&#xff0c;设置代理 set http_proxyhttp://127.0.0.1:7890 & set https_proxyhttp://127.0.0.1:7890注意&#xff1a;使用cmd命令行…

设计链表复习

设计链表 class ListNode {int val;ListNode next;public ListNode() {}public ListNode(int val) {this.val val;}public ListNode(int val, ListNode next) {this.val val;this.next next;}}class MyLinkedList {//size存储链表元素的个数int size;//虚拟头节点ListNode…

十四天学会C++之第七天:STL(标准模板库)

1. STL容器 什么是STL容器&#xff0c;为什么使用它们。向量&#xff08;vector&#xff09;&#xff1a;使用向量存储数据。列表&#xff08;list&#xff09;&#xff1a;使用列表实现双向链表。映射&#xff08;map&#xff09;&#xff1a;使用映射实现键值对存储。 什么…

Swin Transformer V2 Scaling Up Capacity and Resolution(CVPR2022)

文章目录 AbstractIntroduction不稳定性问题下游任务需要的高分辨率问题解决内存问题- Related WorksLanguage networks and scaling upVision networks and scaling upTransferring across window / kernel resolution Swin Transformer V2Swin Transformer简介Scaling Up Mod…

使用TensorRT-LLM进行高性能推理

LLM的火爆之后&#xff0c;英伟达(NVIDIA)也发布了其相关的推理加速引擎TensorRT-LLM。TensorRT是nvidia家的一款高性能深度学习推理SDK。此SDK包含深度学习推理优化器和运行环境,可为深度学习推理应用提供低延迟和高吞吐量。而TensorRT-LLM是在TensorRT基础上针对大模型进一步…

LeetCode2409——统计共同度过的日子数

博主的解法过于冗长&#xff0c;是一直对着不同的案例debug修改出来的&#xff0c;不建议学习。虽然提交成功了&#xff0c;但是自己最后都不知道写的是啥了哈哈哈。 package keepcoding.leetcode.leetcode2409; /*Alice 和 Bob 计划分别去罗马开会。给你四个字符串 arriveA…

【每周一测】Java阶段二第四周学习

目录 1、request中的getParameter(String name)方法的功能是 2、request中的getParameter(String name)方法的功能是 3、spring创建bean对象没有以下哪个方式 4、spring依赖注入中没有以下哪个方式 5、RequestParam、RequestBody、PathVariable的应用场景及区别 6、Cooki…

第三章 网络主机扫描

本章是进入渗透测试工作流程的第一步。无论你是高级还是新手&#xff0c;本章都将帮助你成功地进行网络扫描。在开始扫描网络之前&#xff0c;我们将介绍您需要了解的基础知识。之后&#xff0c;我们将深入研究如何扫描网络目标。本章涵盖以下内容: 一、网络基础 二、识别活主…

BUUCTF 大白 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 看不到图&#xff1f; 是不是屏幕太小了 。 密文&#xff1a; 下载附件后解压&#xff0c;发现一张名为dabai.png的图片。 &#xff08;似乎因为文件被修改过&#xff0c;原图片无法放在这里&#xff0c;这张图片是…

Linux:firewalld防火墙-基础使用(2)

上一章 Linux&#xff1a;firewalld防火墙-介绍&#xff08;1&#xff09;-CSDN博客https://blog.csdn.net/w14768855/article/details/133960695?spm1001.2014.3001.5501 我使用的系统为centos7 firewalld启动停止等操作 systemctl start firewalld 开启防火墙 systemct…

怎么去别人的github工程下载

1、网络 确保网络能够顺利访问github&#xff0c;有的地方的公共网络不能访问github&#xff0c;我之前开过科学上网的会员&#xff0c;发现没必要特意开去访问它。可以直接开手机热点&#xff0c;一般是可以顺利访问的。 2、下载 以我的github开源笔记qq-hh/C_review (gith…

砖家预测:腾讯云双11服务器优惠价格表(新鲜出炉)

2023腾讯云双十一服务器优惠价格表多少钱一年&#xff1f;轻量服务器2核2G3M、2核2G4M、2核4G5M、4核8G12M、8核16G18M、16核32G28M和云服务器CVM标准型S5实例优惠价格&#xff0c;腾讯云百科今年双11服务器价格会在当前的价格基础上享受个9折优惠&#xff0c;可领券 https://c…

网站技术查看

当打开一个网页感觉很好奇&#xff0c;他使用了什么框架和什么技术&#xff1f; 常用的网页技术分析网站。 1. w3techs Check web technologies used by a website - Site InfoW3Techs identifies which web technologies such as CMS, programming language, web server an…

Python中的内存管理:深入分析垃圾回收机制

python中有一个名为refchian的环状双向链表&#xff0c;python运行时创建的所有对象都会添加到refchain中。在refchain中的对象PyObject里都有一个ob_refcnt用来保存当前对象的引用计数器&#xff0c;就是该对象被引用的次数&#xff0c;当对象有新引用时ob_refcnt就会增加&…

最简单修改nacos的修改权重修改上线下线报错

前言 我在docker中部署了一个单体的nacos服务,过了一段时间,由于我导入了另外一个nacos的服务配置导致服务注册没问题,但是服务的修改权重和修改服务的上线下线会报错.于是就有了这篇文章,这篇文章主要是解决上面说的问题 正文 1.报错信息展示(这里我已经修复过了已经展示不了了…

【iOS】简单的网络请求

应iOS小组要求&#xff0c;仿写知乎日报需要实现网络请求并解析JSON格式数据&#xff0c;这篇文章仅对基本的网络请求和iOS中的JSON解析作以记录&#xff0c;还涉及到RunLoop的一点小插曲&#xff0c;具体请求过程和原理以后会详细学习&#xff01;&#x1f64f; 基本网络流程简…