在使用 uni-app 开发过程中,我们经常 会遇到文件上传的需求。然而,uni.uploadFile 方法存在一个限制:它无法在一个请求中上传多个文件。本文将介绍如何在 H5 环境下使用 fetch + FormData 来解决这个问题。
在开发中,我们常常需要一次性上传多个文件,例如:
但是,uni-app 提供的 uni.uploadFile API 只能上传单个文件,即使多次调用也可能导致后端处理复杂。
在 H5 环境中,我们可以使用原生的 fetch 和 FormData 来实现多文件上传。
// 在 H5 环境下上传多文件
function uploadMultipleFiles(files: File[], uploadUrl: string): Promise<any> {
// 检查是否在 H5 环境中
if ((globalThis as any).__PLATFORM__ !== 'h5') {
console.warn('此方法仅支持 H5 环境');
return Promise.reject(new Error('此方法仅支持 H5 环境'));
}
const formData = new FormData();
// 将多个文件添加到 FormData 中
files.forEach((file: File, index: number) => {
// 可以使用相同字段名,后端会接收为数组
formData.append('files', file, file.name);
// 或者使用不同字段名
// formData.append(`file_${index}`, file, file.name);
});
// 添加其他参数
formData.append('userId', '123');
formData.append('category', 'images');
return fetch(uploadUrl, {
method: 'POST',
body: formData
})
.then(response => response.json()) // 注意:fetch 的 then 需先进行 json 处理
.then(data => {
console.log('上传成功:', data);
return data;
})
.catch(error => {
console.error('上传失败:', error);
throw error;
});
}
<template>
<div class="upload-container">
<input
type="file"
ref="fileInput"
multiple
accept="image/*"
@change="handleFileSelect"
/>
<button @click="uploadFiles" :disabled="!selectedFiles.length">
上传选中的 {{ selectedFiles.length }} 个文件
</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const fileInput = ref(null);
const selectedFiles = ref([]);
const handleFileSelect = (event) => {
// 获取用户选择的文件列表
const files = Array.from(event.target.files);
selectedFiles.value = files;
};
const uploadFiles = async () => {
if (!selectedFiles.value.length) {
alert('请先选择文件');
return;
}
// 检查是否在 H5 环境
if (process.client && typeof window !== 'undefined' && window.fetch) {
try {
const result = await uploadMultipleFiles(selectedFiles.value, '/api/upload');
console.log('上传结果:', result);
alert('上传成功');
} catch (error) {
console.error('上传失败:', error);
alert('上传失败: ' + error.message);
}
} else {
alert('此功能仅在 H5 环境下可用');
}
};
// 多文件上传函数
async function uploadMultipleFiles(files, uploadUrl) {
const formData = new FormData();
// 将多个文件添加到 FormData 中
files.forEach(file => {
formData.append('files', file, file.name);
});
// 添加其他参数
formData.append('timestamp', Date.now().toString());
const response = await fetch(uploadUrl, {
method: 'POST',
body: formData
});
// 注意:fetch 的 then 需先进行 json 处理
if (response.ok) {
const result = await response.json();
return result;
} else {
throw new Error(`上传失败,状态码: ${response.status}`);
}
}
</script>
// 带进度的上传实现
interface UploadProgressCallback {
(progress: number): void;
}
interface FileUploadResponse {
[key: string]: any; // 根据实际后端响应类型调整
}
function uploadMultipleFilesWithProgress(
files: File[],
uploadUrl: string,
onProgress?: UploadProgressCallback
): Promise<FileUploadResponse> {
return new Promise((resolve, reject) => {
if ((globalThis as any).__PLATFORM__ !== 'h5') {
reject(new Error('此方法仅支持 H5 环境'));
return;
}
const formData = new FormData();
files.forEach(file => {
formData.append('files', file, file.name);
});
const xhr = new XMLHttpRequest();
// 监听上传进度
xhr.upload.addEventListener('progress', (event: ProgressEvent) => {
if (event.lengthComputable) {
const percentComplete = (event.loaded / event.total) * 100;
onProgress && onProgress(percentComplete);
}
});
xhr.addEventListener('load', () => {
if (xhr.status >= 200 && xhr.status < 300) {
try {
const result = JSON.parse(xhr.response);
resolve(result);
} catch (error) {
reject(new Error('响应解析失败'));
}
} else {
reject(new Error(`上传失败: ${xhr.statusText}`));
}
});
xhr.addEventListener('error', () => {
reject(new Error('网络错误'));
});
xhr.addEventListener('abort', () => {
reject(new Error('上传被取消'));
});
xhr.open('POST', uploadUrl);
xhr.send(formData);
});
}
虽然 uni.uploadFile 有其局限性,但在 H5 环境下,我们可以利用原生的 fetch 和 FormData 来实现多文件上传功能。需要注意的是:
通过这种方式,我们可以在 H5 环境中实现更灵活的多文件上传功能。
(注:文档内容由 Copilot 生成)