vue3实现文件夹上传及存在问题解决

需求是使用 VUE3 实现上传文件夹,其功能主要依赖于 <input> 标签的属性 webkitdirectory

<input
  ref="uploadFolderRef"
  type="file"
  style="display: none"
  @change="submitUpload"
  webkitdirectory
  multiple
/>
<el-button @click="openUploadDialog">上传文件夹</el-button>

将 input 标签的 display 属性设为 none 使其隐藏起来,后续通过点击按钮触发上传框的点击事件:

import { ref } from 'vue'
import { ElMessage } from 'element-plus'

const uploadFolderRef = ref()

const openUploadDialog = () => {
  uploadFolderRef.value.click()
}

const submitUpload = (e: any) => {
  const files = Array.from(e.target.files)
  
  Promise.all(
    files.map((file: any) => {
      const form = new FormData()
      form.append('file', file)
      return UploadApi.upload(form)
    }),
  )
    .then(() => {
      ElMessage.success('上传成功')
    })
    .catch(() => {
      ElMessage.error('上传失败')
    })
}

存在问题:重复上传相同文件夹,只有第一次有效,后续选择文件夹无法完成上传。

原因是上传框的提交上传是 onchange 事件,选择相同的文件夹的 value 属性值是一样的,因此无法触发;

解决方案

  1. 手动修改上传框的 value 属性为空;

    const uploadFolderRef = ref()
    /* 打开上传框之前,先将上传框的value属性设为空 */
    const openUploadDialog = () => {
      uploadFolderRef.value.value = ''
      uploadFolderRef.value.click()
    }
    
  2. 采用 JavaScript 生成上传框,这样将每次都生成新的上传框;

    <template>
        <el-button @click="createFileInput">上传文件夹</el-button>
    </template>
    
    <script setup lang="ts">
    const createFileInput = () => {
      const input = document.createElement('input')
      input.style.display = 'none'
      input.type = 'file'
      input.multiple = true
      input.webkitdirectory = true
      input.onchange = submitUpload
      document.body.appendChild(input)
      input.click()
    }
    </script>
    
### Vue3上传文件夹解决方案 在处理前端应用程序中的文件操作时,遇到的一个常见问题是无法直接通过标准 HTML 文件输入控件选择并上传文件夹。这是因为浏览器的安全策略不允许访问未由用户显式选择的文件资源。 为了实现这一功能,可以考虑以下几种方法: #### 方法一:客户端压缩后再上传 一种可行的方法是在客户端先将整个目录(包括子文件夹)打包成 ZIP 或其他存档格式再上传到服务器端。这可以通过 JavaScript 库来完成,比如 `JSZip` 可用于创建 zip 文件[^1]。 ```javascript import JSZip from &#39;jszip&#39;; import { saveAs } from &#39;file-saver&#39;; async function compressDirectory(directoryHandle) { const zip = new JSZip(); async function traverseFiles(fileSystemHandle, currentPath=&#39;&#39;) { if (fileSystemHandle.kind === "directory") { for await (const entry of fileSystemHandle.values()) { await traverseFiles(entry, `${currentPath}${entry.name}/`); } } else { let content = await fileSystemHandle.getFile(); zip.file(`${currentPath}${content.name}`, content); } } await traverseFiles(directoryHandle); const blob = await zip.generateAsync({ type: "blob" }); saveAs(blob, "archive.zip"); } ``` 这种方法的优点是可以轻松地支持多级嵌套结构,并且能够很好地兼容大多数现代Web应用框架如Vue.js。 #### 方法二:自定义API接口设计 另一种方式是修改后端 API 接口的设计逻辑,在接收文件之前检查是否有特定标志表示这是一个空文件夹。如果检测到了这样的请求,则可以在数据库或其他持久化层记录下该路径的存在而不实际保存任何物理文件[^2]。 对于这两种方案的选择取决于具体的应用场景和技术栈偏好。前者更适合于通用情况下的跨平台开发;后者则可能更适用于内部工具或定制化的业务需求。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风於尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值