更新记录
3.0.0(2025-07-17)
下载此版本
纯API式调用选择器,支持pdf、doc、zip...等所有类型文件
2.6.6(2025-06-04)
下载此版本
更新示例项目
2.6.5(2025-06-04)
下载此版本
屏蔽h5试用提示弹窗
查看更多
平台兼容性
云端兼容性
uni-app(4.71)
Vue2 |
Vue3 |
Chrome |
Safari |
app-vue |
app-nvue |
Android |
iOS |
鸿蒙 |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
- |
微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
快应用-华为 |
快应用-联盟 |
√ |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
其他
lsj-upload 全文件选择上传
右侧按钮导入插件或导入示例项目,导入的是(2.0版 H5+小程序免费),现插件已推出3.0版,完全API式调用选择器,支持pdf、doc、zip...等所有类型文件!
购买须知!!!
本插件为原创作品,已申请软件著作权保护。为尊重开发者权益,请勿未经授权转售或传播。感谢您的理解与支持!
【1.0版(性能低功能少)】 vs 【2.0版(操作复杂)】 vs 【3.0版(功能全面,操作简单,物超所值)】
本插件适用App [ Android、iOS ]、 微信小程序、H5应用
支持所有格式文件选择和上传,使用过程中有任何问题可im联系我或添加我微信,会第一时间回复您
若未及时回复可联系我wx号:lsj274962262
感谢支持!!!
API式调用选择器,支持pdf、doc、zip...等所有类型文件
props
属性 |
是否必填 |
值类型 |
默认值 |
说明 |
debug |
否 |
Boolean |
false |
开启调试日志 |
distinct |
否 |
Boolean |
false |
同名文件是否覆盖 |
API
方法名 |
参数 |
支持平台 |
说明 |
chooseFile |
chooseFile参数说明 |
App/H5/微信小程序 |
文件选择(微信小程序是用的wx.chooseMessageFile) |
upload |
upload参数说明 |
App/H5/微信小程序 |
文件上传 |
getTempFilePath |
文件处理参数说明 |
App/H5/微信小程序 |
获取文件临时路径 |
getBase |
文件处理参数说明 |
App/H5/微信小程序 |
获取文件base64 |
getArrayBuffer |
文件处理参数说明 |
App/H5/微信小程序 |
获取文件ArrayBuffer |
openDocument |
文件处理参数说明 |
App/H5/微信小程序 |
打开文件 |
abort |
file |
App/H5/微信小程序 |
中断上传 |
deleted |
file |
App/H5/微信小程序 |
删除文件 |
clear |
- |
App/H5/微信小程序 |
清空文件 |
参数名 |
类型 |
必填 |
默认值 |
说明 |
count |
Number |
否 |
9 |
允许选择的文件个数 |
size |
Number |
否 |
10 |
单个文件大小上限,单位兆(M) |
multiple |
Boolean |
否 |
true |
是否允许多选(count=1时强制禁止多选) |
formats |
String |
否 |
- |
允许选择的格式,空串等于不限制,多个以逗号隔开,例如 'png,jpg,jpeg' |
accept |
String |
否 |
- |
原生input file属性,控制系统弹框可选文件类型展示 |
success |
Function |
否 |
- |
成功回调 |
fail |
Function |
否 |
- |
失败回调 |
参数名 |
类型 |
必填 |
默认值 |
说明 |
file |
Object |
是 |
- |
chooseFile返回的file |
url |
String |
是 |
- |
上传文件的服务端接口地址 |
name |
String |
否 |
'file' |
服务端接收文件字段的key |
method |
String |
否 |
'post' |
请求协议类型 |
header |
Object |
否 |
- |
HTTP 请求 Header |
formData |
Object |
否 |
- |
HTTP 请求中其他额外的 form data |
onprogress |
Function |
否 |
- |
上传进度回调(持续) |
success |
Function |
否 |
- |
成功回调 |
fail |
Function |
否 |
- |
失败回调 |
参数名 |
类型 |
必填 |
默认值 |
说明 |
file |
Object |
是 |
- |
chooseFile返回的file |
success |
Function |
否 |
- |
成功回调 |
fail |
Function |
否 |
- |
失败回调 |
3.0版示例
<template>
<view>
<lsj-upload debug ref="lsjUploadRef"></lsj-upload>
<view style="padding: 60rpx 30rpx;">
<view style="color: orangered;">
<view style="font-weight: 600;">lsj-upload3.0版</view>API式调用选择器,支持pdf、doc、zip...等所有类型文件
</view>
<view>API</view>
<view>chooseFile: 选择文件</view>
<view>upload: 上传文件</view>
<view>abort: 终止上传</view>
<view>getTempFilePath: 获取文件临时路径</view>
<view>getBase: 获取文件base64</view>
<view>getArrayBuffer: 获取文件ArrayBuffer</view>
<view>openDocument: 打开文件</view>
<view>deleted: 删除文件</view>
<view>clear: 清空文件</view>
</view>
<view style="padding: 8rpx 60rpx;">
<button @click="onClick">chooseFile (选择文件)</button>
</view>
<view style="margin-top: 60rpx;padding: 24rpx;">
<view>已选文件返回files数组,自己写样式</view>
<view v-for="(item,index) in files" :key="index">
<text>文件名:{{ item.name }}</text>
<text style="margin-left: 10rpx;">大小:{{ item.size }}</text>
<text style="margin-left: 10rpx;">状态:{{ item.status }}</text>
<text style="margin-left: 10rpx;">进度:{{ item.progress }}</text>
<text v-if="item.responseText" style="margin-left: 10rpx;">服务端数据:{{ item.responseText }}</text>
<view class="flex">
<text class="flex-item" @click="uploadHandle(item)">上传文件</text>
<text class="flex-item" @click="(item)">终止上传</text>
<text class="flex-item" @click="getBase(item)">获取Base64</text>
<text class="flex-item" @click="getArrayBuffer(item)">获取ArrayBuffer</text>
<text class="flex-item" @click="getTempFilePath(item)">获取临时路径</text>
<text class="flex-item" @click="openDocument(item)">打开文件</text>
<text class="flex-item" @click="del(index)">删除文件</text>
</view>
</view>
</view>
<view style="padding: 60rpx 30rpx;">
<div style="color: blue;" @click="clear">clear (清空所有文件)</div>
</view>
</view>
</template>
<script>
export default {
data() {
return {
title: 'Hello',
files: [],
base64: '',
tempFilePath: ''
}
},
onLoad() {
},
methods: {
// 选择文件
onClick() {
this.$refs.lsjUploadRef.chooseFile({
count: 3, // 文件可选个数
size: 10, // 文件大小上限兆(M)
success:(files) => {
this.files = this.files.concat(files);
console.log('files',files);
// 这个file不是文件流,是插件内需要的file对象,调用插件的其他方法时需传入这个
this.files.forEach(file=> {
// 上传到服务器
if (file.status === 'waiting') {
// 上传
// this.uploadHandle(file);
}
})
},
fail:(err) => {
console.log('选择文件失败');
}
})
},
// 上传到服务器
uploadHandle(file) {
this.$refs.lsjUploadRef.upload({
url: 'http://ttest.shlink.com/ts/v1/oss/upload', // 更换成你自己接口地址
file,
name: 'file',
method: 'post',
header: {
Authorization: ''
},
formData: {},
:(e)=> {
file.status = e.status;
file.progress = e.progress;
console.log('上传中:',e.progress);
},
success:(e) => {
file.status = 'success';
console.log('上传成功',e);
},
fail:(e) => {
file.status = 'fail';
file.progress = 0;
console.log('上传失败',e);
}
});
},
// 获取文件TempFilePath
getTempFilePath(file) {
this.$refs.lsjUploadRef.getTempFilePath({
file,
success:(e) => {
console.log(e.result);
// 图片回显示例
// #ifdef H5
// h5端
// 如果希望回显图片可以用URL.createObjectURL获取blob地址后再image标签src
this.tempFilePath = URL.createObjectURL(e.result);
// #endif
// 其他端不需要URL.createObjectURL
// #ifndef H5
this.tempFilePath = e.result;
// #endif
// 临时路径用于uni.uploadFile上传的示例
uni.uploadFile({
url: 'http://ttest.shlink.com/ts/v1/oss/upload', // 更换成你自己接口地址
header: {
Authorization: 'bearer '
},
success:(e) => {
console.log('success',e);
},
fail: (err) => {
console.log('fail',err);
},
// #ifdef H5
file: e.result
// #endif
// #ifndef H5
filePath: e.result
// #endif
})
}
});
},
// 获取文件base64
getBase(file) {
this.$refs.lsjUploadRef.getBase({
file,
success:(e) => {
console.log('getBase',e);
// #ifndef MP
this.base64 = e.result;
// #endif
// #ifdef MP
this.base64 = 'data:image/png;base64,' + e.result;
// #endif
}
});
},
// 获取文件ArrayBuffer
getArrayBuffer(file) {
this.$refs.lsjUploadRef.getArrayBuffer({
file,
success:(e) => {
console.log('getArrayBuffer',e);
}
});
},
// 打开文件
openDocument(file) {
this.$refs.lsjUploadRef.openDocument({
file,
success:(e) => {
console.log('打开文件成功');
}
});
},
// 终止上传文件
(file) {
if (file.status === '') {
this.$refs.lsjUploadRef.abort(file);
file.progress = 0;
console.log('已终止上传:',file.name);
}
},
// 删除指定文件
del(index) {
this.$refs.lsjUploadRef.deleted(this.files[index]);
this.files.splice(index,1);
},
// 清空所有文件
clear() {
this.$refs.lsjUploadRef.clear();
this.files = [];
},
}
}
</script>
<style scoped>
.flex {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.flex-item {
flex: 0 0 calc(33.333% - 10px);
box-sizing: border-box;
border: 1px solid blue;
display: flex;
justify-content: center;
align-items: center;
min-height: 2em;
}
</style>
-
-
插件使用注意事项
2.0版通过webview实现,不支持返回文件的本地路径,上传文件必须使用组件内置的upload函数
组件如果在scroll-view内使用需要自己监听scroll事件,并在滚动结束的时候调用一次show(查看下方scroll示例)!!
控件的height、width应与slot自定义内容宽高度保持一致。
nvue窗口只能使用固定模式position=absolute 。
show() 当DOM重排后在this.$nextTick内调用show(),控件定位会更加准确。
hide() APP端webview层级比view高,如不希望触发点击时,应调用hide隐藏控件,反之调用show。
APP端请优先联调Android,上传成功后再运行iOS端。
若iOS端跨域服务端同学实在配置不好,可把hybrid下html目录放到服务器去,同源则不存在跨域问题,或者toBase传true(以base64格式上传)。
小程序端因hybrid不能使用本地HTML,所以插件提供的是从微信消息列表拉取文件并选择,请知悉。
上传报status:0 问题排查
App如果上传报status: 0,调试方法:关闭manifest.json>h5的代理(若有配置代理),并运行项目至Chrome浏览器,按F12查看接口详细报错信息(重点:是Chrome,不是HX内置浏览器)
Android端不存在跨域问题,如上传报status: 0时
1、如果能进入接口,但收不到任何参数,检查是否在option>header里传入了Content-Type属性(默认不需要传,会自动匹配,若接口要求必须传则需要与接口匹配)
2、如果未能进入接口,检查nginx网关配置,不清楚可加群查看群相册资料。
iOS端若出现status:0,排除上面两个问题后只会是跨域问题
1、后端处理允许上传接口跨域。(前端无需修改)
2、若后端不放开跨域,可将hybrid>html文件夹放入与接口同源的服务器,同源访问则不存在跨域问题。(可加群查看群相册资料)
使用说明
属性 |
是否必填 |
值类型 |
默认值 |
说明 |
permission |
否 |
String |
组件内查看 |
需要校验的系统权限集合 |
isPermissionInToast |
否 |
Boolean |
true |
未授权时是否显示内置Toast提示权限permission[].message,建议传true |
isPermissionInModal |
否 |
Boolean |
true |
用户拒绝授权时是否显示内置弹框引导用户开启权限 |
width |
否 |
String |
100% |
容器宽度(App必填且与slot内容宽度一致) |
height |
是 |
String |
80rpx |
容器高度(App必填且与slot内容高度一致) |
debug |
否 |
Boolean |
false |
打印调试日志,传true时会将透明层显示(app端显示黑色的块、h5显示input),便于观察调试 |
option |
是 |
Object |
- |
文件上传接口相关参数 |
toBase |
否 |
Boolean |
false |
【2.3.8新增】选择的文件是否转base64上传 |
instantly |
否 |
Boolean |
true |
instantly=true:选择文件后自动上传,instantly=false不自动上传,可通过调用upload()函数上传所有待上传的文件 |
distinct |
否 |
Boolean |
false |
【2.3.0新增】是否去重同名文件(提示:不去重复时前端展示同名文件名称后面+(N),后端接收到的file.name还是为原名) |
count |
否 |
Number |
10 |
文件选择上限(个) |
size |
否 |
Number |
10 |
文件大小上限(M) |
multiple |
否 |
Boolean |
true |
是否允许多选 |
wxFileType |
否 |
String |
all |
微信小程序文件选择器格式限制(all=从所有文件选择,video=只能选择视频文件,image=只能选择图片文件,file=可以选择除了图片和视频之外的其它的文件) |
accept |
否 |
String |
- |
文件选择器input file格式限制(有效值自行百度查accept) |
formats |
否 |
String |
- |
限制允许上传的格式,空串=不限制,默认为空,多个格式以逗号隔开,例如png,jpg,pdf |
childId |
否 |
String |
lsjUpload |
自定义组件id,在回调函数第二个参数返回(若页面有多个组件可用于区分是哪个组件回调) |
position |
否 |
String |
static |
控件的定位模式(static=控件随页面滚动;absolute=控件在页面中绝对定位,不随窗口内容滚动) |
top,left,right,bottom |
否 |
[Number,String] |
0 |
设置控件绝对位置,position=absolute时有效 |
@changeFile |
否 |
Function |
Map |
选择文件后触发,返回所有已选择文件Map集合 |
@progress |
否 |
Function |
Object |
上传中持续触发,返回正在上传的文件对象,可通过set更新至页面显示上传进度 |
@uploadEnd |
否 |
Function |
Object |
上传结束回调,返回当前上传的文件对象,type等于success(上传成功)返回responseText属性(服务端返回数据) |
@permissionBefore |
否 |
Function |
Object |
某项权限未授权时触发回调,返回{permission,message},仅isPermissionInToast传false时生效 |
@permissionFail |
否 |
Function |
Object |
用户拒绝授权时触发回调,返回{permission,message,result} |
【2.3.0】新增uni.$emit事件监听
方法名 |
说明 |
$upload-show |
调用当前页面所有上传组件的show() |
$upload-hide |
调用当前页面所有上传组件的hide() |
使用示例
this.$nextTick(()=>{
// 更新当前页所有上传组件在页面中的位置
uni.$emit('$upload-show',{});
})
$Refs
作用 |
方法名 |
传入参数 |
说明 |
显示和定位控件点击层 |
show |
- |
控件显示状态下可触发点击 |
隐藏控件点击层 |
hide |
- |
控件隐藏状态下不触发点击 |
动态设置文件列表 |
setFiles |
[Array,Map] files |
传入格式请与组件选择返回格式保持一致,且name为必须属性,可查看下方演示 |
动态更新参数 |
setData |
[String] name,[any] value |
name支持a.b 和 a[b],可查看下方演示 |
移除选择的文件 |
clear |
[String] name |
不传参数清空所有文件,传入文件name时删除该name的文件 |
手动上传 |
upload |
[String] name |
不传参数默认依次上传所有type=waiting的文件,传入文件name时不关心type是否为waiting,单独上传指定name的文件 |
参数 |
是否必填 |
说明 |
url |
是 |
上传接口地址 |
name |
否 |
上传接口文件key,默认为file |
header |
否 |
上传接口请求头 |
formData |
否 |
上传接口额外参数 |
progress返回对象字段说明
字段 |
说明 |
file |
文件对象 |
name |
文件名称 |
size |
文件大小 |
path |
用于image标签src属性回显图片 |
type |
文件上传状态:waiting(等待上传)、loading(上传中)、success(成功) 、fail(失败) |
responseText |
上传成功后服务端返回数据(仅type为success时存在) |
以下演示为vue窗口使用方式,nvue使用区别是必须传入控件绝对位置如top,bottom,left,right,且position只能为absolute,如不清楚可点击右侧导入示例项目有详细演示代码。
vue:
<view>
<view class="header-bg">
<view class="header">基本使用示例</view>
</view>
<view style="padding: 30rpx;" v-for="(v,i) in data" :key="'data_'+i">{{v}}</view>
<!-- #ifndef MP-WEIXIN -->
<view v-for="(item,index) in files.values()" :key="'file_'+index">
<image style="width: 100rpx;height: 100rpx;" :src="item.path" mode="widthFix"></image>
<text>{{ item.name }}</text>
<text style="margin-left: 10rpx;">大小:{{ item.size }}</text>
<text style="margin-left: 10rpx;">状态:{{ item.type }}</text>
<text style="margin-left: 10rpx;">进度:{{ item.progress }}</text>
<text v-if="item.responseText" style="margin-left: 10rpx;">服务端数据:{{ item.responseText }}</text>
<text style="margin-left: 10rpx;padding: 0 10rpx;border: 1rpx solid #007AFF;" @click="clear(item.name)">删除</text>
</view>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<view v-for="(item,index) in wxFiles" :key="'file_'+index">
<text>{{ item.name }}</text>
<text style="margin-left: 10rpx;">大小:{{ item.size }}</text>
<text style="margin-left: 10rpx;">状态:{{ item.type }}</text>
<text style="margin-left: 10rpx;">进度:{{ item.progress }}</text>
<view>
<button @click="clear(item.name)">删除</button>
</view>
</view>
<!-- #endif -->
<lsj-upload
ref="lsjUploadRef"
debug
:count="count"
:width="width"
:height="height"
:option="option"
:toBase="toBase"
@changeFile="onChange"
@progress=""
@uploadEnd="onuploadEnd"
>
<view style="background-color: #007AFF;" :style="{width: width,height: height}">自定义选择按钮</view>
</lsj-upload>
<view class="bottom-btn flex">
<button @click="add">在按钮上方添加数据</button>
<button @click="onsetFiles">setFiles同步文件</button>
</view>
</view>
export default {
data() {
return {
data: ['DOM重排演示'],
option: {
// 上传服务器地址,该地址非真实路径,需替换为你项目自己的接口地址
url: 'http://iestest.com/dropbox/document/upload',
// 上传附件的key
name: 'file',
// 请求头,默认不要写content-type,让浏览器自适配
header: {},
// 额外参数
formData: {}
},
width: '180rpx',
height: '180rpx',
count: 9,
toBase: false,
// 文件回显列表
files: new Map(),
}
},
onReady() {
// 模拟异步请求数据并在按钮上方渲染
setTimeout(()=> {
this.add();
},2000)
},
methods: {
onsetFiles() {
let files1 = [{name: '测试文件名称.png'},{name: '测试文件名称2.png',}];
let files2 = new Map();
files2.set('测试文件名称.png',{name: '测试文件名称.png'});
this.$refs.lsjUploadRef.setFiles(files2)
},
add() {
this.data.push('在按钮上方动态添加了数据,此时需要调用组件的show刷新透明层top');
this.$nextTick(()=> {
uni.$emit('$upload-show',{});
})
},
// 移除某个文件
clear(name) {
// name=指定文件名,不传name默认移除所有文件
this.$refs.lsjUploadRef.clear(name);
},
// 某文件上传结束回调(成功失败都回调)
onuploadEnd(item,childId) {
console.log(`${item.name}已上传结束,上传状态=${item.type}`);
// 更新当前窗口状态变化的文件
this.files.set(item.name,item);
// ---可删除--演示上传完成后取服务端数据
if (item['responseText']) {
console.log('演示服务器返回的字符串JSON转Object对象');
this.files.get(item.name).responseText = JSON.parse(item.responseText);
}
// 微信小程序Map对象for循环不显示,所以转成普通数组,
// 如果你用不惯Map对象,也可以像这样转普通数组,组件使用Map主要是避免反复文件去重操作
// #ifdef MP-WEIXIN
this.wxFiles = [...this.files.values()];
// #endif
// 强制更新视图
this.$forceUpdate();
// ---可删除--演示判断是否所有文件均已上传成功
let isAll = [...this.files.values()].find(item=>item.type!=='success');
if (!isAll) {
console.log('已全部上传完毕');
}
else {
console.log(isAll.name+'待上传');
}
},
// 上传进度回调
(item,childId) {
// 更新当前状态变化的文件
this.files.set(item.name,item);
// 微信小程序Map对象for循环不显示,所以转成普通数组
// #ifdef MP-WEIXIN
this.wxFiles = [...this.files.values()];
// #endif
// 强制更新视图
this.$forceUpdate();
},
// 文件选择回调
onChange(files,childId) {
console.log('已选择的文件Map',JSON.stringify([...files]));
// 更新选择的文件
this.files = files;
// 强制更新视图
this.$forceUpdate();
// 微信小程序Map对象for循环不显示,所以转成普通数组,不要问为什么,我也不知道
// #ifdef MP-WEIXIN
this.wxFiles = [...this.files.values()];
// #endif
// ---可删除--演示重新定位覆盖层控件
this.$nextTick(()=>{
console.log('演示重新定位 (提示:像示例里文件列表在按钮上方时就需要插入文件后更新webview位置)');
// 直接更新当前页面所有上传组件webview位置
uni.$emit('$upload-show',{});
});
}
}
}
温馨提示
- 文件上传
- APP端请优先联调Android,上传成功后再运行iOS端,如iOS返回status=0则需要后端开启允许跨域;
- header的Content-Type类型需要与服务端要求一致,否则收不到附件(服务端若没有明文规定则“不传”,使用默认匹配)
- 服务端不清楚怎么配置跨域可加群咨询
- 欢迎加入QQ讨论群:
交流群4:413918560
交流群3:667530868
交流群2:469580165
交流群1:701468256