[前言]
uniapp目前本身并不支持直接获取媒体文件列表,不过因为提供了原生插件使用和开发的功能,问题并不大,接下来进入正题。

Android 10以上的存储机制

因为Android 10以上高版本,开始执行更为安全分区存储机制,对外部存储文件访问方式重新设计,便于用户更好的管理外部存储文件。这个机制遵循三个原则:
1、文件由哪个应用创建,应用不需要存储权限即可以访问应用自己创建文件(这点不错)
2、添加外部存储应用私有目录文件访问限制,现在即使你的应用申请读写权限也不能访问其他应用外部存储私有目录文件(让数据更安全,防止篡改)
3、添加pdf、office、doc等非媒体、图片和音频文件的访问限制,你的应用即使申请了读写权限也无法访问其他应用创建的pdf、office、doc等文件

在这里插入图片描述

MediaStore API

MediaStore是Android系统提供的媒体库,可以通过ContentResolver来对媒体库执行查询、修改、删除文件的操作。本文开发的原生插件就是基于此API.

下面是uniapp调用层的方法,在这个方法里用Android代码逻辑编写插件功能。

// 获取文件的中枢函数
@UniJSMethod(uiThread = true)
    public void getFiles(final JSONObject options, UniJSCallback callback,UniJSCallback fail) {
        JSONObject param = new JSONObject();
        param.put("fileType","image");

        if(options != null){
            param = options;
        }

        JSONObject data = new JSONObject();
        String fileType = param.getString("fileType");

        ContentResolver resolver = mUniSDKInstance.getContext().getContentResolver();
        switch (fileType){
            case "image": data = FileManageUtils.getAllPhoto(resolver); break;
            case "video": data = FileManageUtils.getAllVideo(resolver); break;
            case "audio": data = FileManageUtils.getAllMusic(resolver); break;
            case "document": data = FileManageUtils.getAllText(resolver); break;
            case "zip": data = FileManageUtils.getAllZip(resolver); break;
        }

	 // 到这里已经获取到了文件列表数据, 通过uniapp传过来的回调返回数据
        if(callback != null){
            callback.invoke(data);
        }else{
            if(fail != null){
                JSONObject result = new JSONObject();
                result.put("code",201);
                result.put("message","获取文件列表失败");
                fail.invoke(result);
            }
        }
    }
获取照片文件列表

对每种类型都进行了一层函数封装,getAllPhoto获取所有图片,getAllVideo获取所有视频,getAllMusic获取或有音频,getAllText获取所有文档,getAllZip获取所有压缩文件。

	// 获取文件列表并进行一个简单封装,返回 list / 长度 / 媒体文件类型
    public static JSONObject getAllPhoto(ContentResolver resolver) {
        String[] projection = new String[]{MediaStore.Images.ImageColumns._ID,MediaStore.Images.ImageColumns.SIZE, MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.DISPLAY_NAME};
        List<JSONObject> list = getFiles(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,resolver,null,null,projection,MediaStore.Images.ImageColumns.DISPLAY_NAME,MediaStore.Images.ImageColumns.DATA,MediaStore.Images.ImageColumns.SIZE,MediaStore.Images.ImageColumns._ID);
        JSONObject data = new JSONObject();
        data.put("fileList",list);
        data.put("fileNum",list.size());
        data.put("fileType","image");
        return data;
    }
getFile函数的核心代码

这个方法通过外部传入的类型和字段等参数来查询媒体文件,获取一个游标索引,从中可以循环取出数据封装成目标格式。

private static List<JSONObject> getFiles(Uri uri, ContentResolver resolver, String selection, String[] selectionArgs, String[] projection, String colName, String colPath, String colSize, String colId){
        List<JSONObject> files = new ArrayList<>();

        //projection 是定义返回的数据,selection 通常的sql 语句,例如  selection=MediaStore.Images.ImageColumns.MIME_TYPE+"=? " 那么 selectionArgs=new String[]{"jpg"};
        Cursor cursor = resolver.query(uri, projection, selection, selectionArgs, MediaStore.Images.ImageColumns.DATE_MODIFIED + "  desc");

        float fileSize = 0;
        long fileId = 0;
        String fileName;
        String filePath;

        while (cursor.moveToNext()) {
            fileSize = cursor.getFloat(cursor.getColumnIndex(colSize));
            fileName = cursor.getString(cursor.getColumnIndex(colName));
            filePath = cursor.getString(cursor.getColumnIndex(colPath));
            fileId = cursor.getLong(cursor.getColumnIndex(colId));

            JSONObject obj = new JSONObject();
            obj.put("path",filePath);
            obj.put("size",fileSize);
            obj.put("name",fileName);
            obj.put("id",fileId);
            files.add(obj);
        }

        cursor.close();

        return files;
    }

uniapp层调用
<template>
	<view>
		<uni-segmented-control 
			:values="['图片','视频','音频','文档','zip']" 
			styleType="button" 
			@clickItem="onClickItem"
			class="segmented-style">
		</uni-segmented-control>
		
		<view class="list-style" v-if="listData && listData.length > 0">
			<view v-for="(item, index) in listData" v-bind:key="index" class="list-item-style" @longpress="longPressEvent(item)">
				<text class="list-item-text">{{getUnitValue(item.size)}}</text>
				<image :src="item.path" class="list-item-media-style" v-if="mode === 'image'"></image>
				<view v-else-if="mode === 'video'" style="background-color: bisque;display: flex;align-items: center;justify-content: center;" class="list-item-media-style">
					<image src="../../static/icon_play.png" style="width: 50rpx; height: 50rpx;"></image>
				</view>
				
				<view v-else-if="mode === 'audio'" style="background-color: indianred;display: flex;align-items: center;justify-content: center;" class="list-item-media-style">
					<image src="../../static/icon_audio.png" style="width: 50rpx; height: 50rpx;"></image>
				</view>
			</view>
		</view>
		<view v-else style="color: #999999;min-height: calc(60vh);display: flex;align-items: center;justify-content: center;">
			设备中没有发现该类型文件
		</view>
	</view>
</template>

<script>
	var fileManage = uni.requireNativePlugin("luanqing-file-manage");
	
	export default {
		data() {
			return {
				mode:'image', // 0|图片 1|视频  2|音频  3|文件
				listData:[],
			}
		},
		onShow() {
			this.loadFiles();
		},
		methods: {
			longPressEvent(data){
				const $that = this;
				uni.showModal({
					title:'是否确定删除?',
					cancelText:'取消',
					confirmText:'删除',
					complete(res) {
						if(res.confirm){
							console.error("长按删除:",data);
							fileManage.deleteFile({fileType:$that.mode,fileId:data.id, filePath:data.path},(res=>{
								console.error("删除成功:",res);
								$that.loadFiles();
							}), (res=>{
								console.error("删除失败:",res);
							}));
						}
					}
				})
			},
			loadFiles(){
				const $that = this;
				console.error("是否是空:",fileManage === undefined || fileManage === null);
				fileManage.getFiles({fileType:this.mode}, (res=>{
					console.error("接口返回的数据:",res.files);
					$that.listData = res.fileList;
				}), (fail=>{
					console.error("获取文件列表失败:",fail);
				}));
			},
			onClickItem(e){
				this.mode = this.getFileType(e.currentIndex);
				this.loadFiles();
			},
			// 获取文件类型
			getFileType(typeId){
				switch(typeId){
					case 0: return "image";
					case 1: return "video";
					case 2: return "audio";
					case 3: return "document";
					case 4: return "zip";
					default: return "image";
				}
			},
			// 计算文件大小值和单位
			getUnitValue(value){
				if(value > 1000000)
					return (value / 1000000).toFixed(1) + 'MB';
				else if(value > 1000)
					return Math.floor(value / 1000).toFixed(1) + 'KB';
				else if(value < 1000)		
					return Math.floor(value / 1000).toFixed(1) + 'KB';
				// return value > 1000 ? 
			}
		}
	}
</script>

<style>
	.segmented-style{
		margin-left: 20rpx;
		margin-right: 20rpx;
		margin-top: 25rpx;
	}
	.list-style{
		display: flex;
		flex-direction: row;
		flex-wrap: wrap;
	}
	.list-item-style{
		width: 115rpx;
		height: 115rpx;
		margin: 5rpx;
		position: relative;
		background-color: #ffffff;
	}
	.list-item-media-style{
		width: 115rpx;
		height: 115rpx;
		position: absolute;
		top: 0rpx;
		bottom: 0rpx;
		left: 0rpx;
		right: 0rpx;
		z-index: 10;
	}
	.list-item-text{
		z-index: 100;
		position: absolute;
		bottom: 0rpx;
		right: 0rpx;
		color: #333333;
		padding: 0rpx 10rpx;
		font-size: 20rpx;
		background-color: #F2F2F2;
	}
</style>

链接: 下载地址

示例图:在这里插入图片描述在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐