当前位置: 首页 > news >正文

Android文件选择器[超级轻量级FilePicker测试没有问题][挣扎解决自带文件管理器获取不到绝对地址问题而是返回msf%3A1000038197]

超级轻量级FilePicker测试没有问题

本文摘录于:https://blog.csdn.net/gitblog_00365/article/details/141449437只是做学习备份之用,绝无抄袭之意,有疑惑请联系本人!
今天真的是发了疯的找文件管理器,因为调用系统自带的文件管理器老是有问题,所以越调越着急,最后发现还是FilePicker简单好用,代码仓库地址:https://gitcode.com/gh_mirrors/andr/android-filepicker/overview?utm_source=artical_gitcode&index=top&type=card&webUrl
步骤很简单,第一步添加依赖
首先,在你的项目的 build.gradle (Module) 文件中的 dependencies 块添加如下依赖:

implementation 'com.github.angads25:filepicker:1.1.0'

然后在代码黄总增加如下内容(原文内容):

import com.angads25.filepicker.model.FilePath;
import com.angads25.filepicker.view.FilePickerFragment;...// 在某个方法内启动FilePicker
private void chooseFile() {FilePickerFragment filePickerFragment = FilePickerFragment.newInstance();filePickerFragment.setFileType(FilePickerFragment.FILE_TYPE_ALL);filePickerFragment.setOnFilePathSelectedListener(new FilePickerFragment.OnFilePathSelectedListener() {@Overridepublic void onFilePathSelected(List<FilePath> filePaths) {// 处理选择的文件路径for (FilePath fp : filePaths) {Log.d("MyApp", "Selected Path: " + fp.getPath());}}});filePickerFragment.show(getSupportFragmentManager(), "FILE_PICKER");
}

我自己的代码如下:

public void onClickSelectFile(View view) {Toast.makeText(getApplicationContext(), "请选择bin格式文件", Toast.LENGTH_SHORT).show();DialogProperties properties = new DialogProperties();properties.selection_mode = DialogConfigs.SINGLE_MODE;properties.selection_type = DialogConfigs.FILE_SELECT;properties.root = new File("/sdcard");properties.error_dir = new File(DialogConfigs.DEFAULT_DIR);properties.offset = new File(DialogConfigs.DEFAULT_DIR);String[] extensions = {"bin",};properties.extensions = extensions;FilePickerDialog dialog = new FilePickerDialog(OTA_Active.this, properties);dialog.setTitle("请选择bin格式文件");dialog.setDialogSelectionListener(new DialogSelectionListener() {@Overridepublic void onSelectedFilePaths(String[] files) {// files数组包含了用户由应用程序选择的文件路径。String path=files[0];if(path!="") {int start = path.lastIndexOf("/");if (start != -1 ) {FileNameList.add(path.substring(start + 1));FilePathList.add(path);ArrayAdapter<String> array = new ArrayAdapter(OTA_Active.this, android.R.layout.simple_list_item_1, FileNameList);listview.setAdapter(array);OTA_Active.this.runOnUiThread(new Runnable() {@Overridepublic void run() {//此时已在主线程中,可以更新UI了AdapterView.OnItemClickListener onItemClickListener = listview.getOnItemClickListener();if(onItemClickListener!=null){onItemClickListener.onItemClick(listview,null,listview.getCount()-1,0);}}});}}}});dialog.show();}

本章节的源码可以从如下地址下载:https://download.csdn.net/download/chengdong1314/89943454
测试确实可行,使用起来完全没问题,视频操作演示如下:

选择文件操作

挣扎解决自带文件管理器获取不到绝对地址问题

非常无奈按照网上的方法调用系统自带的文件管理器选择文件的时候总会这样返回msf%3A1000038197之类的数据,比如如下:

content://com.android.providers.downloads.documents/document/msf%3A1000038197

本文摘录于:https://cloud.tencent.com/developer/article/2003648只是做学习备份之用,绝无抄袭之意,有疑惑请联系本人!
这里根据上面的文章调用如下函数:

    public static File uriToFileApiQ(Uri uri, Context context) {File file = null;if (uri == null) return file;//android10以上转换if (uri.getScheme().equals(ContentResolver.SCHEME_FILE)) {file = new File(uri.getPath());} else if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {//把文件复制到沙盒目录ContentResolver contentResolver = context.getContentResolver();String displayName = System.currentTimeMillis() + Math.round((Math.random() + 1) * 1000)+ "." + MimeTypeMap.getSingleton().getExtensionFromMimeType(contentResolver.getType(uri));try {InputStream is = contentResolver.openInputStream(uri);File cache = new File(context.getCacheDir().getAbsolutePath(), displayName);FileOutputStream fos = new FileOutputStream(cache);FileUtils.copy(is, fos);file = cache;fos.close();is.close();} catch (IOException e) {e.printStackTrace();}}return file;}

这样调用uriToFileApiQ函数:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (resultCode == Activity.RESULT_OK) {Uri uri = data.getData();if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {//4.4以后File fi = uriToFileApiQ(this, uri);path=fi.getPath();} else {//4.4以下下系统调用方法path = getRealPathFromURI(uri);}int index = path.lastIndexOf('.');if (index > 0) {if(!("bin".equals(path.substring(index + 1))))return;}else  return;if(path!="") {int start = path.lastIndexOf("/");if (start != -1 ) {FileNameList.add(path.substring(start + 1));FilePathList.add(path);ArrayAdapter<String> array = new ArrayAdapter(OTA_Active.this, android.R.layout.simple_list_item_1, FileNameList);listview.setAdapter(array);AdapterView.OnItemClickListener onItemClickListener = listview.getOnItemClickListener();if(onItemClickListener!=null){onItemClickListener.onItemClick(listview,null,listview.getCount()-1,0);}}}}}

onActivityResult是这样引起的:

    public void onClickSelectFile(View view) {Toast.makeText(getApplicationContext(), "请选择bin格式文件", Toast.LENGTH_SHORT).show();Intent intent = new Intent(Intent.ACTION_GET_CONTENT);intent.setType("*/*");//无类型限制intent.addCategory(Intent.CATEGORY_OPENABLE);startActivityForResult(intent, 1);}

上面的uriToFileApiQ函数是有缺陷的,FileUtils.copy(is, fos);函数报错了,如果改成FileUtils.copy(is.tostring(), fos.tostring());确实编译没错了,但是拷贝到沙盒里面的文件大小是0,拷贝出错!
在这里插入图片描述
无奈啊,我这时候都放弃了,又去本文第一节去尝试了一圈,搞定了自定义文件管理器的问题,但是客户又说一定要自带的资源管理器,可以打开最近的文件,无奈只能够死磕这个问题!!!
最后找呀找,找到这篇文章:https://www.bytezonex.com/archives/b8w3-cvz.html
这里说明了原来的拷贝错误的原因:
Android 10 获取文件路径难题:/document/msf: 解决方案

许多开发者在 Android 10 设备上使用 Intent.ACTION_GET_CONTENT 获取文件路径时,都会遇到返回路径形如 /document/msf: 的情况,令人困惑不已。这并非应用本身的错误,而是 Android 10 为了加强隐私保护而采取的新策略。本文将深入剖析这一变化背后的原因,并提供一套完整的解决方案,帮助你绕过障碍,顺利获取目标文件。

从文件路径到 URI:Android 10 文件访问机制的转变
在 Android 10 之前,应用能够通过文件路径直接访问设备上的绝大多数文件,这种方式虽然便捷,但也存在着巨大的安全隐患,容易造成用户隐私泄露。为了提升系统安全性,Android 10 引入了分区存储机制,限制应用直接访问外部存储空间。

新的机制下,当应用使用 Intent.ACTION_GET_CONTENT 选择文件时,系统不再返回直接的文件路径,而是返回一个 content:// 格式的 URI。这个 URI 指向一个由 MediaStore 管理的中间层,应用无法直接从中获取文件路径,相当于在应用和真实文件之间建立了一道安全屏障。

神秘的 /document/msf: :特殊文件的安全处理
/document/msf: 正是 Android 10 返回的一种特殊 URI,它通常代表着应用选择的并非媒体文件(例如图片、视频),而是其他类型的文件。由于系统无法确定应用如何使用这些文件,为了最大程度地保护用户隐私,系统选择不直接提供文件路径,而是返回这个特殊的 URI,将文件访问权限牢牢掌握在自己手中。

ContentResolver 和 InputStream:突破封锁的利器
想要在 Android 10 上顺利获取文件内容,我们需要借助 Android 系统提供的 ContentResolver 和 InputStream 这两大利器。

操作步骤:

获取 ContentResolver: 通过 context.contentResolver 获取 ContentResolver 实例,相当于获取了与 MediaStore 通信的桥梁。
打开 InputStream: 使用 ContentResolver.openInputStream(uri) 方法,将获取的 content:// URI 传递进去,打开一个指向目标文件的 InputStream,相当于在安全屏障上打开了一条通道,文件内容将通过这条通道流向应用。
读取文件内容: 使用 InputStream 的 read() 方法,如同读取水流一般,将文件内容源源不断地读取出来,供应用使用。
整体思路就是通过InputStream 把数据从外部储存转写到沙盒来,最后修改uriToFileApiQ函数如下:

   public static File uriToFileApiQ(Context context,Uri uri) {File file = null;if(uri == null) return file;//android10以上转换if (uri.getScheme().equals(ContentResolver.SCHEME_FILE)) {file = new File(uri.getPath());} else if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {//把文件复制到沙盒目录ContentResolver contentResolver = context.getContentResolver();
//            String displayName = System.currentTimeMillis()+ Math.round((Math.random() + 1) * 1000)
//                    +"."+ MimeTypeMap.getSingleton().getExtensionFromMimeType(contentResolver.getType(uri));//            注释掉的方法可以获取到原文件的文件名,但是比较耗时Cursor cursor = contentResolver.query(uri, null, null, null, null);String displayName="";if (cursor.moveToFirst()) {displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));}try {InputStream in = contentResolver.openInputStream(uri);File cache = new File(context.getCacheDir().getAbsolutePath(), displayName);FileOutputStream out = new FileOutputStream(cache);//FileUtils.copy(in.toString(), out.toString());int len=-1;byte[] b=new byte[512*1024];while((len=in.read(b))!=-1){out.write(b,0,len);}file = cache;in.close();out.close();} catch (IOException e) {e.printStackTrace();}}return file;}

这里测试到拷贝过来的文件大小就对了,到此问题成功解决!
在这里插入图片描述
本章节的安卓源码从如下地址下载:https://download.csdn.net/download/chengdong1314/89943485
视频操作如下:

相关文章:

Android文件选择器[超级轻量级FilePicker测试没有问题][挣扎解决自带文件管理器获取不到绝对地址问题而是返回msf%3A1000038197]

超级轻量级FilePicker测试没有问题 本文摘录于&#xff1a;https://blog.csdn.net/gitblog_00365/article/details/141449437只是做学习备份之用&#xff0c;绝无抄袭之意&#xff0c;有疑惑请联系本人&#xff01; 今天真的是发了疯的找文件管理器,因为调用系统自带的文件管理…...

【论文速读】| RED QUEEN: 保护大语言模型免受隐蔽多轮越狱攻击

基本信息 原文标题&#xff1a;RED QUEEN: Safeguarding Large Language Models against Concealed Multi-Turn Jailbreaking 原文作者&#xff1a;Yifan Jiang, Kriti Aggarwal, Tanmay Laud, Kashif Munir, Jay Pujara, Subhabrata Mukherjee 作者单位&#xff1a;Hippocr…...

39.第二阶段x86游戏实战2-HOOK实现主线程调用

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要…...

wordpress argon主题美化方面

1、页面前端额外CSS&#xff1a; /*字体*/ font-face {font-family: myFont1; src:url(https://blog.yangmumu.com/css/fonts/Dancing.ttf) ;font-display: swap; } font-face {font-family: myFont2; src:url(https://blog.yangmumu.com/css/fonts/Regular.ttf) ;font-displa…...

qt QRadioButton详解

QRadioButton 是一个可以切换选中&#xff08;checked&#xff09;或未选中&#xff08;unchecked&#xff09;状态的选项按钮。单选按钮通常呈现给用户一个“多选一”的选择&#xff0c;即在一组单选按钮中&#xff0c;一次只能选中一个按钮。 重要方法 QRadioButton(QWidget…...

Qt 最小化,最大化,关闭窗口

Qt 最小化,最大化 在Qt中&#xff0c;你可以使用QWidget类提供的方法来实现窗口的最小化、最大化等操作。 最小化窗口 你可以使用QWidget的showMinimized()方法来最小化窗口。这将隐藏窗口并将其显示为系统托盘区域的图标。 connect(ui->btnMin,&QPushButton::click…...

【vue项目中添加告警音频提示音】

一、前提&#xff1a; 由于浏览器限制不能自动触发音频文件播放&#xff0c;所以实现此类功能时&#xff0c;需要添加触发事件&#xff0c;举例如下&#xff1a; 1、页面添加打开告警声音开关按钮 2、首次进入页面时添加交互弹窗提示&#xff1a;是否允许播放音频 以上两种方…...

百度SEO分析实用指南 提升网站搜索排名的有效策略

内容概要 在数字化时代&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;已经成为提升网站曝光度的关键工具。本指南将带您了解SEO的基本知识&#xff0c;帮助您在复杂的网络环境中立足。我们将从关键词优化开始&#xff0c;重点讲解如何选择合适的关键词来提高搜索引擎排…...

高并发场景下的性能测试方法!

在现代互联网应用中&#xff0c;高并发场景下的性能测试显得尤为重要。无论是电商平台的秒杀活动&#xff0c;还是社交应用的突发流量&#xff0c;都需要确保系统能够在高并发情况下稳定运行。本文将详细介绍高并发场景下的性能测试方法&#xff0c;并提供具体的方案和实战演练…...

杂项——USB键盘与鼠标流量分析——BUUCTF——流量分析

第一次做USB键盘与鼠标流量分析的题目&#xff0c;现在来好好做一个总结 1. 基础知识 USB流量指的是USB设备接口的流量&#xff0c;攻击者能够通过监听usb接口流量获取键盘敲击键、鼠标移动与点击、存储设备的铭文传输通信、USB无线网卡网络传输内容等等。 在正式介绍 USB H…...

Java如何实现企业微信审批流程

大家好&#xff0c;我是 V 哥。最近的一个项目中&#xff0c;用到企业微信的审批流程&#xff0c;整理出来分享给大家。在企业微信中实现审批流程可以通过调用企业微信的开放API完成&#xff0c;企业微信提供了审批应用接口&#xff0c;用于创建审批模板、发起审批流程以及获取…...

GEE app:在地图上构建一个可以查看局部的小窗

目录 简介 函数 ee.Geometry.MultiLineString(coords, proj, geodesic, maxError) Arguments: Returns: Geometry.MultiLineString getBounds(asGeoJSON) Arguments: Returns: GeoJSONGeometry|List|String setControlVisibility(all, layerList, zoomControl, scaleC…...

leetcode71:简化路径

给你一个字符串 path &#xff0c;表示指向某一文件或目录的 Unix 风格 绝对路径 &#xff08;以 / 开头&#xff09;&#xff0c;请你将其转化为 更加简洁的规范路径。 在 Unix 风格的文件系统中规则如下&#xff1a; 一个点 . 表示当前目录本身。此外&#xff0c;两个点 ..…...

nodejs入门教程4:nodejs创建第一个应用

1. 安装 Node.js 首先&#xff0c;确保你的计算机上已经安装了 Node.js。如果还没有安装&#xff0c;可以从官方网站&#xff08;https://nodejs.org&#xff09;下载并安装最新的 LTS 版本。安装完成后&#xff0c;你可以在命令行或终端中运行以下命令来验证安装&#xff1a;…...

启用 iPhone 原生的五笔输入

聊聊如何在 iOS 中使用原生的五笔输入法 本文虽然介绍的是如何添加五笔键盘&#xff0c;其实其他键盘&#xff08;双拼&#xff0c;外语键盘、第三方输入法&#xff09;也是类似的添加方式。 ‍ 使用原生的理由 虽然之前的文章列了不少第三方的五笔输入法&#xff0c;但其实…...

这个工具让你轻松开发一个带AI功能的Notion

这个工具让你轻松开发一个带AI功能的Notion Plate 是一款由 AI 加持的富文本编辑器&#xff0c;旨在帮助开发者创建功能强大的 WYSIWYG 文本编辑工具。本文将介绍 Plate 的基本信息、特点、以及如何快速上手使用。 软件简介 Plate 由 udecode 开发&#xff0c;基于 React 和 S…...

光耦合器的关键作用和创新---腾恩科技

光耦合器或光隔离器已成为电路中必不可少的器件&#xff0c;它允许信号在无需直接电接触的情况下跨不同电压域传输。这种隔离能力对于保护低压元件免受高压电路的潜在损坏至关重要。本文将仔细研究光耦合器在当今技术中发挥的独特作用&#xff0c;并探讨其在各种应用中不断扩展…...

穿越死锁的迷雾:pthread_mutex_lock的终极挑战与破解策略

穿越死锁的迷雾:pthread_mutex_lock的终极挑战与破解策略 一、死锁的基本概念二、pthread_mutex_lock 出现死锁的原因三、pthread_mutex_lock 出现死锁的表现四、处理pthread_mutex_lock 出现死锁的方法1. 避免死锁1.1 遵循锁的顺序原则1.2 使用定时锁1.3 使用尝试锁1.4 使用递…...

Dockerfile制作Oracle19c镜像

Dockerfile文件 cat > Dockerfile << EOF # 使用 Oracle Linux 8 作为基础镜像 FROM oraclelinux:8# 复制 Oracle 19c 安装包 COPY oracle-database-ee-19c-1.0-1.x86_64.rpm /tmp/# 安装 Oracle 19c 数据库和依赖 RUN yum localinstall -y /tmp/oracle-database-ee-…...

【时间之外】IT人求职和创业应知【23】

目录 新闻一:央行发布首个买断式逆回购交易公告 新闻二:2024CCF科技创业大赛报名截止 新闻三:BNB Chain将在迪拜主办第四届BNB孵化联盟(BIA) 认知决定你的赚钱能力。以下是今天可能影响你求职和创业的热点新闻: 今日关键字:TCL两连扳,已经跑了,我的认知就到此了 新…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中&#xff0c;集合判空是一个常见但容易出错的场景。传统方式虽然可行&#xff0c;但存在一些潜在问题&#xff1a; // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...

「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案

在移动互联网营销竞争白热化的当下&#xff0c;推客小程序系统凭借其裂变传播、精准营销等特性&#xff0c;成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径&#xff0c;助力开发者打造具有市场竞争力的营销工具。​ 一、系统核心功能架构&…...