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>


回答:

这有一个npm库:https://www.npmjs.com/package/p-limit
它可以帮助我们轻松搞定并发,它的使用也非常简单,不清楚可以自己搜索一下使用


回答:

如果要求不高的话,直接 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);

}

以上是 vue+js 如何限制并发上传数? 的全部内容, 来源链接: utcz.com/p/934958.html

回到顶部