前端上载文件时,上载多个文件,但是一个一个调用接口,怎么实现

在前端实现多个文件逐个上传,可以通过以下几种方式实现:

1. 使用 async/await 顺序上传

复制代码
async function uploadFilesSequentially(files) {
  for (let i = 0; i < files.length; i++) {
    try {
      const formData = new FormData();
      formData.append('file', files[i]);
      formData.append('filename', files[i].name);
      
      const response = await fetch('/api/upload', {
        method: 'POST',
        body: formData
      });
      
      const result = await response.json();
      console.log(`文件 ${files[i].name} 上传成功`, result);
      
      // 可以在这里更新上传进度
      updateProgress(i + 1, files.length);
      
    } catch (error) {
      console.error(`文件 ${files[i].name} 上传失败:`, error);
      // 可以选择继续上传剩余文件,或停止上传
      // break; // 如果上传失败就停止
    }
  }
  console.log('所有文件上传完成');
}

2. 使用递归实现顺序上传

复制代码
function uploadFilesRecursively(files, index = 0) {
  if (index >= files.length) {
    console.log('所有文件上传完成');
    return Promise.resolve();
  }
  
  const file = files[index];
  const formData = new FormData();
  formData.append('file', file);
  
  return fetch('/api/upload', {
    method: 'POST',
    body: formData
  })
  .then(response => response.json())
  .then(result => {
    console.log(`文件 ${file.name} 上传成功`);
    updateProgress(index + 1, files.length);
    
    // 继续上传下一个文件
    return uploadFilesRecursively(files, index + 1);
  })
  .catch(error => {
    console.error(`文件 ${file.name} 上传失败:`, error);
    // 可以选择继续上传或停止
    return uploadFilesRecursively(files, index + 1);
  });
}

3. 完整的组件示例

复制代码
<template>
  <div>
    <input 
      type="file" 
      multiple 
      @change="handleFileChange"
    />
    <button @click="uploadFiles">上传文件</button>
    
    <!-- 进度显示 -->
    <div v-if="uploading">
      <div>上传进度: {{ uploadedCount }} / {{ totalFiles }}</div>
      <div>当前文件: {{ currentFileName }}</div>
      <progress 
        :value="uploadedCount" 
        :max="totalFiles"
      ></progress>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import axios from 'axios';

const files = ref([]);
const uploading = ref(false);
const uploadedCount = ref(0);
const totalFiles = ref(0);
const currentFileName = ref('');

// 处理文件选择
const handleFileChange = (event) => {
  files.value = Array.from(event.target.files);
};

// 顺序上传文件
const uploadFiles = async () => {
  if (!files.value.length) return;
  
  uploading.value = true;
  uploadedCount.value = 0;
  totalFiles.value = files.value.length;
  
  for (let i = 0; i < files.value.length; i++) {
    const file = files.value[i];
    currentFileName.value = file.name;
    
    try {
      await uploadSingleFile(file);
      uploadedCount.value++;
      
    } catch (error) {
      console.error(`文件 ${file.name} 上传失败:`, error);
      // 可以在这里处理错误,比如记录失败的文件
    }
  }
  
  uploading.value = false;
  console.log('所有文件上传完成');
};

// 上传单个文件
const uploadSingleFile = async (file) => {
  const formData = new FormData();
  formData.append('file', file);
  formData.append('timestamp', Date.now());
  
  // 可以添加其他参数
  formData.append('userId', '123');
  formData.append('category', 'documents');
  
  const response = await axios.post('/api/upload', formData, {
    headers: {
      'Content-Type': 'multipart/form-data'
    },
    // 显示上传进度(可选)
    onUploadProgress: (progressEvent) => {
      const percent = Math.round(
        (progressEvent.loaded * 100) / progressEvent.total
      );
      console.log(`文件 ${file.name} 上传进度: ${percent}%`);
    }
  });
  
  return response.data;
};
</script>

4. 使用 Promise.reduce 实现顺序执行

复制代码
import axios from 'axios';

async function sequentialUpload(files) {
  const results = await files.reduce(async (previousPromise, file, index) => {
    const previousResults = await previousPromise;
    
    console.log(`开始上传第 ${index + 1} 个文件: ${file.name}`);
    
    try {
      const formData = new FormData();
      formData.append('file', file);
      
      const response = await axios.post('/api/upload', formData);
      previousResults.push({
        file: file.name,
        success: true,
        data: response.data
      });
    } catch (error) {
      previousResults.push({
        file: file.name,
        success: false,
        error: error.message
      });
    }
    
    return previousResults;
  }, Promise.resolve([]));
  
  return results;
}

5. 并发控制(推荐)

如果需要兼顾效率和顺序,可以使用并发控制:

复制代码
class FileUploader {
  constructor(maxConcurrent = 1) {
    this.maxConcurrent = maxConcurrent; // 最大并发数
    this.queue = [];
    this.activeCount = 0;
    this.results = [];
  }
  
  addFile(file) {
    this.queue.push(file);
  }
  
  async uploadAll() {
    const batches = [];
    
    // 将文件分批
    for (let i = 0; i < this.queue.length; i += this.maxConcurrent) {
      batches.push(this.queue.slice(i, i + this.maxConcurrent));
    }
    
    // 按批次顺序执行
    for (const batch of batches) {
      const batchPromises = batch.map(file => this.uploadSingle(file));
      const batchResults = await Promise.allSettled(batchPromises);
      this.results.push(...batchResults);
    }
    
    return this.results;
  }
  
  async uploadSingle(file) {
    const formData = new FormData();
    formData.append('file', file);
    
    return axios.post('/api/upload', formData);
  }
}

// 使用示例
const uploader = new FileUploader(3); // 最多3个并发
files.forEach(file => uploader.addFile(file));
uploader.uploadAll().then(results => {
  console.log('上传结果:', results);
});

关键注意事项:

  1. 错误处理:每个文件上传都应该有独立的错误处理

  2. 进度反馈:给用户显示上传进度

  3. 取消上传:提供取消上传的功能

  4. 重试机制:对失败的上传提供重试功能

  5. 性能优化:大文件可以考虑分片上传

  6. 内存管理:及时释放不再需要的文件对象

选择哪种方案取决于你的具体需求:

  • 如果需要严格的顺序执行,使用方案1或2

  • 如果需要更好的性能,可以使用方案5的并发控制

  • 如果需要在Vue/React中使用,可以结合框架的特性

相关推荐
可问春风_ren2 小时前
前端文件上传详细解析
前端·ecmascript·reactjs·js
羊小猪~~3 小时前
【QT】--文件操作
前端·数据库·c++·后端·qt·qt6.3
晚风资源组4 小时前
CSS文字和图片在容器内垂直居中的简单方法
前端·css·css3
Miketutu4 小时前
Flutter学习 - 组件通信与网络请求Dio
开发语言·前端·javascript
光影少年6 小时前
前端如何调用gpu渲染,提升gpu渲染
前端·aigc·web·ai编程
Surplusx7 小时前
运用VS Code前端开发工具完成网页头部导航栏
前端·html
小宇的天下7 小时前
Calibre 3Dstack --每日一个命令day13【enclosure】(3-13)
服务器·前端·数据库
一只小bit8 小时前
Qt 文件:QFile 文件读写与管理教程
前端·c++·qt·gui
午安~婉8 小时前
整理知识点
前端·javascript·vue