欢迎光临
我们一直在努力

uni-app——uni-app小程序附件上传的文件类型限制问题

小程序附件上传的文件类型限制问题

问题背景

在小程序开发中,"附件上传"是常见功能。但很多开发者在实现时会遇到一个问题:用户只能选择图片,无法选择PDF、Word等其他类型的文件。

最近在开发审批功能时就遇到了这个问题:审批申请需要上传附件(如合同、发票等),但用户反馈只能选择图片,无法选择其他文件。

问题现象

期望效果:
┌─────────────────────────────────────┐
│ 添加附件 │
│ [图片] [PDF] [Word] [Excel] … │
└─────────────────────────────────────┘

实际效果:
┌─────────────────────────────────────┐
│ 添加附件 │
│ [图片] [图片] [图片] (只能选图片) │
└─────────────────────────────────────┘

问题根因

小程序文件选择 API 对比

小程序提供了多个文件选择 API,各有不同的用途和限制:

API用途支持的文件类型平台支持
uni.chooseImage() 选择图片 仅图片(jpg/png/gif等) 全平台
uni.chooseVideo() 选择视频 仅视频(mp4等) 全平台
uni.chooseFile() 选择文件 多种文件类型 H5/App(小程序不支持)
uni.chooseMessageFile() 从聊天记录选择 多种文件类型 仅微信小程序

问题代码

// 错误写法:只能选择图片
const chooseFile = () => {
uni.chooseImage({
count: 9,
success: (res) => {
// 只能获取到图片
uploadFiles(res.tempFilePaths)
}
})
}

问题在于使用了 chooseImage,这个 API 顾名思义只能选择图片。

解决方案

方案一:使用 chooseMessageFile(微信小程序推荐)

微信小程序专属 API,允许用户从微信聊天记录中选择文件:

const chooseFile = () => {
uni.chooseMessageFile({
count: 9,
type: 'file', // 'all' | 'image' | 'video' | 'file'
success: (res) => {
// res.tempFiles 包含文件信息
// [{ path, size, name, type }]
const files = res.tempFiles.map(file => ({
path: file.path,
name: file.name,
size: file.size,
type: file.type
}))
uploadFiles(files)
}
})
}

优点:

  • 支持选择各种文件类型(PDF、Word、Excel等)
  • 用户可以从聊天记录快速选择已有文件
  • 体验流畅,符合微信用户习惯

缺点:

  • 仅微信小程序支持
  • 文件必须存在于聊天记录中

方案二:组合多个 API

提供多种选择入口,满足不同需求:

<template>
<view class="upload-section">
<view class="upload-title">添加附件</view>
<view class="upload-buttons">
<button @click="chooseImage">选择图片</button>
<button @click="chooseFromChat">从聊天记录选择</button>
</view>

<!– 已选文件列表 –>
<view class="file-list">
<view
v-for="(file, index) in fileList"
:key="index"
class="file-item"
>
<image
v-if="isImage(file)"
:src="file.path"
class="file-thumb"
/>
<view v-else class="file-icon">
{{ getFileIcon(file.name) }}
</view>
<text class="file-name">{{ file.name }}</text>
<text class="file-delete" @click="removeFile(index)">删除</text>
</view>
</view>
</view>
</template>

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

const fileList = ref([])

// 选择图片
const chooseImage = () => {
uni.chooseImage({
count: 9 – fileList.value.length,
success: (res) => {
const newFiles = res.tempFilePaths.map((path, index) => ({
path,
name: `图片${fileList.value.length + index + 1}.jpg`,
type: 'image'
}))
fileList.value.push(…newFiles)
}
})
}

// 从聊天记录选择(微信小程序)
const chooseFromChat = () => {
// #ifdef MP-WEIXIN
uni.chooseMessageFile({
count: 9 – fileList.value.length,
type: 'file',
success: (res) => {
const newFiles = res.tempFiles.map(file => ({
path: file.path,
name: file.name,
size: file.size,
type: getFileType(file.name)
}))
fileList.value.push(…newFiles)
}
})
// #endif

// #ifndef MP-WEIXIN
uni.showToast({
title: '当前平台不支持',
icon: 'none'
})
// #endif
}

// 判断是否为图片
const isImage = (file) => {
return file.type === 'image' || /\\.(jpg|jpeg|png|gif|webp)$/i.test(file.name)
}

// 获取文件图标
const getFileIcon = (fileName) => {
const ext = fileName.split('.').pop().toLowerCase()
const iconMap = {
pdf: '📄',
doc: '📝',
docx: '📝',
xls: '📊',
xlsx: '📊',
ppt: '📽️',
pptx: '📽️',
zip: '📦',
rar: '📦'
}
return iconMap[ext] || '📎'
}

// 获取文件类型
const getFileType = (fileName) => {
if (/\\.(jpg|jpeg|png|gif|webp)$/i.test(fileName)) return 'image'
if (/\\.(mp4|mov|avi)$/i.test(fileName)) return 'video'
return 'file'
}

// 删除文件
const removeFile = (index) => {
fileList.value.splice(index, 1)
}
</script>

方案三:跨平台兼容封装

封装一个通用的文件选择函数,自动适配不同平台:

// utils/chooseFile.js

/**
* 跨平台文件选择
* @param {Object} options
* @param {number} options.count – 最大选择数量
* @param {string} options.type – 文件类型:'all' | 'image' | 'video' | 'file'
* @returns {Promise<Array>} 文件列表
*/

export const chooseFile = (options = {}) => {
const { count = 9, type = 'all' } = options

return new Promise((resolve, reject) => {
// 微信小程序:使用 chooseMessageFile
// #ifdef MP-WEIXIN
if (type === 'image') {
uni.chooseImage({
count,
success: (res) => {
resolve(res.tempFilePaths.map((path, i) => ({
path,
name: `image_${Date.now()}_${i}.jpg`,
type: 'image'
})))
},
fail: reject
})
} else {
uni.chooseMessageFile({
count,
type: type === 'all' ? 'all' : type,
success: (res) => {
resolve(res.tempFiles.map(file => ({
path: file.path,
name: file.name,
size: file.size,
type: file.type
})))
},
fail: reject
})
}
// #endif

// H5/App:使用 chooseFile
// #ifdef H5 || APP-PLUS
uni.chooseFile({
count,
type: type === 'all' ? 'all' : type,
success: (res) => {
resolve(res.tempFiles.map(file => ({
path: file.path,
name: file.name,
size: file.size,
type: file.type
})))
},
fail: reject
})
// #endif
})
}

使用方式:

import { chooseFile } from '@/utils/chooseFile'

// 选择任意文件
const files = await chooseFile({ count: 5, type: 'all' })

// 只选择图片
const images = await chooseFile({ count: 9, type: 'image' })

文件上传的完整流程

选择文件后,还需要上传到服务器:

/**
* 上传文件到服务器
* @param {Array} files – 文件列表
* @returns {Promise<Array>} 上传结果
*/

export const uploadFiles = async (files) => {
const uploadTasks = files.map(file => {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: 'https://api.example.com/upload',
filePath: file.path,
name: 'file',
formData: {
fileName: file.name,
fileType: file.type
},
success: (res) => {
const data = JSON.parse(res.data)
resolve({
…file,
url: data.url, // 服务器返回的文件URL
id: data.id
})
},
fail: reject
})
})
})

return Promise.all(uploadTasks)
}

注意事项

1. 文件大小限制

小程序对上传文件大小有限制,建议在选择后进行校验:

const MAX_FILE_SIZE = 10 * 1024 * 1024 // 10MB

const validateFileSize = (files) => {
const oversizedFiles = files.filter(f => f.size > MAX_FILE_SIZE)
if (oversizedFiles.length > 0) {
uni.showToast({
title: `文件大小不能超过10MB`,
icon: 'none'
})
return false
}
return true
}

2. 文件类型校验

防止用户上传不支持的文件类型:

const ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx', 'xls', 'xlsx']

const validateFileType = (files) => {
const invalidFiles = files.filter(f => {
const ext = f.name.split('.').pop().toLowerCase()
return !ALLOWED_EXTENSIONS.includes(ext)
})

if (invalidFiles.length > 0) {
uni.showToast({
title: `不支持的文件类型`,
icon: 'none'
})
return false
}
return true
}

3. 用户体验优化

  • 显示上传进度
  • 支持取消上传
  • 上传失败自动重试

const uploadWithProgress = (file, onProgress) => {
return new Promise((resolve, reject) => {
const task = uni.uploadFile({
url: 'https://api.example.com/upload',
filePath: file.path,
name: 'file',
success: (res) => resolve(JSON.parse(res.data)),
fail: reject
})

// 监听上传进度
task.onProgressUpdate((res) => {
onProgress && onProgress(res.progress)
})
})
}

总结

  • API 选择很重要:chooseImage 只能选图片,需要选择其他文件类型时应使用 chooseMessageFile(微信)或 chooseFile(H5/App)

  • 平台差异需处理:不同平台支持的 API 不同,建议封装统一的文件选择函数

  • 完善的校验机制:文件大小、文件类型都需要校验,避免上传失败或服务器压力

  • 良好的用户体验:提供多种选择方式、显示上传进度、处理异常情况


  • 本文源于实际项目中的问题修复经验,希望对遇到类似问题的开发者有所帮助。

    赞(0)
    未经允许不得转载:171主机测评 » uni-app——uni-app小程序附件上传的文件类型限制问题
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址