vue+js 如何限制并发上传数?

有这么一个需求:需要上传几千张图片到后端接口

使用 python 的时候,可以线程池来实现并发控制

import os
from loguru import logger
from pathlib import Path
from PIL import Image
from mark import BASE_DIR
from PIL import UnidentifiedImageError
import requests
from concurrent.futures.thread import ThreadPoolExecutor


pool = ThreadPoolExecutor(10)

images_dir: Path = Path(
    '/Volumes/MyPassport/iv测试图片集')


def get_file_size(file_path: Path) -> int:
    return os.path.getsize(file_path)


formats = ('jpg', 'jpeg', 'png')

images = [images_dir /
          f for f in os.listdir(images_dir) if f.endswith(formats) and not f.startswith('.')]

images.sort()

logger.debug(f'一共有 {len(images)} 个图片')


def func(index: int, image: Image):
    try:
        logger.debug(f'{index}/{len(images)-1} {image.name}')

        with open(image, 'rb') as file:
            response = requests.post(
                'http://127.0.0.1:6200/meta/image/file',
                files={
                    'file': file
                }, data={
                    'network': 'vgg16'
                })
            assert response.status_code == 200

    except (UnidentifiedImageError, OSError) as error:
        logger.warning(error)
        os.remove(image)


for index, image in enumerate(images):
    pool.submit(func, index, image)

pool.shutdown(wait=True)

但是使用 vue+js 的时候,我不知道可以怎么操作

我用 vue+js 写的函数如下

const handleUpload = () => {
    fileList_1.value.forEach((item, index) => {
        const formData = new FormData();
        formData.append("file", item.originFileObj);

        console.log(`第 ${index + 1} 个文件上传`, item.name);

        axios
            .post("/api/meta/image/file", formData)
            .then((response) => {
                if (response.status === 500) {
                    console.error(`第 ${index + 1} 个文件上传失败:`, response.data);
                    message.error(response.data.message);
                } else {
                    responseBody.value = response.data;
                    responseData.value = response.data;
                    message.success(item.name + " 上传成功", 2);
                }
            })
            .catch((error) => {
                console.error('捕捉到错误了', error);
                message.error(error.response.data.message);
            });
    })
};

这个函数有一个问题,当在浏览器同时选中 1000 个图片,然后点击上传的时候,貌似会同时发出 1000 个 HTTP 请求,这个是非常危险的,而且会让浏览器变得非常的卡顿

我希望可以实现:不管需要上传多少个文件,并发上传数不超过 10

我的 vue+js 完整代码如下

<template>
    <top-bar></top-bar>

    <div class="container">
        <div class="container-item">
            <div>
                <a-row>
                    <a-typography-title>使用说明:</a-typography-title>
                </a-row>
                <a-row>
                    <a-typography-paragraph> 选择图片 </a-typography-paragraph>
                </a-row>

                <a-row>
                    <a-typography-paragraph>
                        评分范围在 0-100 分,100 分是满分。如果图片相似度太小,会出现负分
                    </a-typography-paragraph>
                </a-row>

                <a-row>
                    <br />
                </a-row>

                <a-row>
                    <a-col :span="24" style="text-align: center">
                        <a-upload v-model:file-list="fileList_1" name="avatar" list-type="picture-card"
                            class="avatar-uploader" :before-upload="handleBeforeUpload1" @preview="handlePreview"
                            :multiple="true">
                            <div>
                                <!-- <loading-outlined v-if="loading"></loading-outlined>
                <plus-outlined v-else></plus-outlined> -->
                                <div class="ant-upload-text">选择图片</div>
                            </div>
                        </a-upload>
                    </a-col>
                </a-row>

                <a-row>
                    <a-col :span="24" style="text-align: center">
                        <a-button type="primary" @click="handleUpload" style="text-align: center">上传</a-button>
                    </a-col>
                </a-row>
            </div>
        </div>
    </div>

    <div class="container" v-if="responseBody">
        <div class="container-item">
        </div>
    </div>

    <div class="container">
        <div class="container-item">
            <a-list item-layout="vertical" :data-source="responseData">
                <template #renderItem="{ item }">
                    <div>
                        <img width="272" alt="logo" :src=item.file_url />
                        <p>{{ item.hash_code }}</p>
                    </div>
                </template>
            </a-list>
        </div>
    </div>

    <a-modal :visible="previewVisible" :title="previewTitle" :footer="null" @cancel="handleCancel">
        <img alt="example" style="width: 100%" :src="previewImage" />
    </a-modal>
</template>

<script setup>
import { PlusOutlined, LoadingOutlined } from "@ant-design/icons-vue";
import { ref, reactive } from "vue";
import { message } from "ant-design-vue";
import { UploadOutlined } from "@ant-design/icons-vue";
import axios from "axios";
import { onMounted } from "vue";
import router from "@/router";

onMounted(async () => {
    document.title = "图片录入"; // 设置浏览器标签页的标题
});
function getBase64(file) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = () => resolve(reader.result);
        reader.onerror = (error) => reject(error);
    });
}

const fileList_1 = ref([]);

const handleBeforeUpload1 = (file) => {
    // fileList_1.value.splice(0);
    return false; // 阻止默认上传行为
};

const previewVisible = ref(false);
const previewImage = ref("");
const previewTitle = ref("");

const handleCancel = () => {
    previewVisible.value = false;
    previewTitle.value = "";
};
const handlePreview = async (file) => {
    if (!file.url && !file.preview) {
        file.preview = await getBase64(file.originFileObj);
    }
    previewImage.value = file.url || file.preview;
    previewVisible.value = true;
    previewTitle.value =
        file.name || file.url.substring(file.url.lastIndexOf("/") + 1);
};

const responseBody = ref(null);
const responseData = ref([]);


const handleUpload = () => {
    fileList_1.value.forEach((item, index) => {
        const formData = new FormData();
        formData.append("file", item.originFileObj);

        console.log(`第 ${index + 1} 个文件上传`, item.name);

        axios
            .post("/api/meta/image/file", formData)
            .then((response) => {
                if (response.status === 500) {
                    console.error(`第 ${index + 1} 个文件上传失败:`, response.data);
                    message.error(response.data.message);
                } else {
                    responseBody.value = response.data;
                    responseData.value = response.data;
                    message.success(item.name + " 上传成功", 2);
                }
            })
            .catch((error) => {
                console.error('捕捉到错误了', error);
                message.error(error.response.data.message);
            });
    })
};



const navigateToRoot = () => {
    router.push("/");
};
</script>

<style>
body {
    background-color: #e9ecef !important;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

.gray-placeholder {
    color: gray;
}

.container {
    margin: 0 auto;
    /* 居中显示 */
    margin-top: 20px;
    max-width: 1440px;
    /* 设置最大宽度为900px */
    background-color: #ffffff;
    /* 浅灰色 */
    border-radius: 0.25rem;
}

.container-item {
    padding: 25px;
    border-width: 0 0 1px;
    margin-bottom: 20px;
}

.theme-icon {
    width: 64px;
    /* 设置图标的宽度 */
    height: 64px;
    /* 设置图标的高度 */
}

.avatar-uploader>.ant-upload {
    width: 128px;
    height: 128px;
}

.ant-upload-select-picture-card i {
    font-size: 32px;
    color: #999;
}

.ant-upload-select-picture-card .ant-upload-text {
    margin-top: 8px;
    color: #666;
}
</style>

阅读 1.1k
2 个回答

如果要求不高的话,直接 Promise.all() 就可以了吧。

for (let i = 0, len = Math.ceil(images.length / 10); i < len; i++) {
  const uploads = [];
  for (let j = 0; j < 10; j++) {
    uploads.push(doUpload(images[i * 10 + j]));
  }
  await Promise.all(uploads);
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题