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

WPS二次开发系列:如何使用WPS返回的FileUri

作者持续关注 WPS二次开发专题系列,持续为大家带来更多有价值的WPS开发技术细节,如果能够帮助到您,请帮忙来个一键三连,更多问题请联系我(QQ:250325397)

目录

什么是FileUri

在SDK中的使用场景

打开文档时候

文档保存/事件时候

第三方应用中使用FileUri

什么是FileUri

若要安全地将应用中的文件提供给其他应用,您需要配置应用,以内容 URI 的形式提供文件的安全句柄。Android FileProvider 组件会根据您在 XML 中指定的内容生成文件的内容 URI。

详细请参考 WPS二次开发系列:如何对打开文档路径FileUri授权

在SDK中的使用场景

打开文档时候

在SDK调用打开文档前,需要主要将文件本地路径filePath转换成fileUri,详见WPS二次开发系列:如何对打开文档路径FileUri授权

文档保存/事件时候

文档保存/关闭事件在SDK的回调接口中均返回了WPS编辑保存后的文档路径(fileUri), 这个FileUri是WPS临时授权给第三方应用使用的FileUri,那么该如何在其它第三方应用中使用呢?

文档保存/关闭事件:

FileApi fileApi = WpsSdk.getInstance().getApi(FileApi.class);
if (fileApi != null) {fileApi.addEventListener(SingleOpenActivity.this, ApiEvent.DocumentAfterSave, new EventListener() {@Overridepublic void onEvent(String eventName, Bundle bundle) {//注意在高版本Android系统中,第三方应用是无法读取WPS私有路径,需要通过获取WPS提供的URI来访问文档数据Uri currentFileUri = bundle.getParcelable("CurrentFileUri");Log.d("WpsSdk", "demo onEvent DocumentAfterSave : s=" + s + " bundle=" + bundle + " path=" + path+ " currentFileUri="+currentFileUri);}});fileApi.addEventListener(SingleOpenActivity.this, ApiEvent.DocumentAfterClose, new EventListener() {@Overridepublic void onEvent(String s, Bundle bundle) {Uri currentFileUri = bundle.getParcelable("CurrentFileUri");Log.d("WpsSdk", "demo onEvent DocumentAfterClose : s=" + s + " bundle=" + bundle + " path=" + path + " currentFileUri=" + currentFileUri);Utils.showToast(SingleOpenActivity.this, "文档路径:" + currentFileUri);}});//注意在调用文档打开之前进行注册fileApi.openFile(this, fileUri, bundle);
}

注意上面示例代码中返回的:currentFileUri 就是WPS授权给第三方使用的文件路径

第三方应用中使用FileUri

那么如何在第三方应用中使用curentFileUri呢,这里关键是需要通过

InputStream inputStream = context.getContentResolver().openInputStream(uri);

来进行读写。

详见Demo使用参考,根据SDK返回的currentFileUri路径,将文件从WPS沙盒空间拷贝文件到第三方应用自己的私有空间,如Android/data/<包名>/files/output/ 目录下

String outFile = FileUtil.copyFileFromUri(MainActivity.this, fileUri, "output");

完整拷贝工具类参考示例代码:

package cn.wps.sdk.demo.util;import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import android.text.TextUtils;import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;public class FileUtil {private static final int IO_BUFFER_SIZE    = 8192;public static String getFileName(String filePath){if (null == filePath)return null;return new File(filePath).getName();}/**** @param uri 文件uri* @param fileDir 沙盒目录 files/fileDir* @return 沙盒路径*/public static String copyFileFromUri(Context context, Uri uri, String fileDir) {File dirFile = getFileDir(context, fileDir);String fileName = parseFileName(context, uri);String destPath = dirFile.getAbsolutePath() + File.separator + fileName;if (new File(destPath).exists()) {new File(destPath).deleteOnExit();}InputStream inputStream = null;try {inputStream = context.getContentResolver().openInputStream(uri);} catch (FileNotFoundException e) {throw new RuntimeException(e);}if (copyFile(inputStream, destPath)) {return destPath;}return null;}public static boolean copyFile(InputStream in, String destPath){return copyFile(in, destPath, true);}public static boolean copyFile(InputStream in, String destPath, boolean closeInput){if (in == null || destPath == null)return false;File outFile = new File(destPath);if (!prepareFilePath(outFile))return false;FileOutputStream os = null;try{os = new FileOutputStream(outFile);copy(in, os);return true;}catch (Exception e){e.printStackTrace();}finally{closeQuietly(os);if (closeInput) closeQuietly(in);}return false;}public static void copy(InputStream in, OutputStream out) throws IOException{copy(in, out, null);}public static void copy(InputStream in, OutputStream out, byte[] buffer) throws IOException{if (null == buffer)buffer = new byte[IO_BUFFER_SIZE];int read;while ((read = in.read(buffer)) != -1){out.write(buffer, 0, read);}}private static boolean prepareFilePath(File file){File parent = file.getParentFile();assert parent != null;if (parent.exists()) {return true;}return parent.mkdirs();}public static void closeQuietly(FileOutputStream out){if (null == out)return;try {out.flush();try {out.getFD().sync(); // bug 314867} catch (Exception ignore) {}out.close();} catch (IOException ignore){}}public static void closeQuietly(Closeable closable){if (null == closable)return;try {if (closable instanceof FileOutputStream){FileOutputStream _out = (FileOutputStream)closable;_out.flush();try {_out.getFD().sync();} catch (Exception ignore) {}}else if (closable instanceof RandomAccessFile){RandomAccessFile _out = (RandomAccessFile)closable;try {_out.getFD().sync();} catch (Exception ignore) {}}closable.close();} catch (IOException ignore){}}private static String parseFileName(Context context, Uri uri) {if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {return StringUtil.getNamePart(uri.getPath());}String name = queryDisplayName(context, uri);if (!TextUtils.isEmpty(name)) {return name;}name = queryOtherColumn(context, uri);if (!TextUtils.isEmpty(name)) {return name;}if (TextUtils.isEmpty(name)) {String urlPath = uri.getPath();String namePart = StringUtil.getNamePart(urlPath);String extPart = StringUtil.pathExtension(urlPath);if (!TextUtils.isEmpty(namePart) && !TextUtils.isEmpty(extPart)) {name = namePart;}}return name;}private static String queryDisplayName(Context context, Uri uri) {ContentResolver resolver = context.getContentResolver();Cursor cur = null;String name = null;try {cur = resolver.query(uri, new String[]{MediaStore.MediaColumns.DISPLAY_NAME}, null, null, null);if (cur != null && cur.getCount() == 1 && cur.moveToFirst()) {final int displayNameIndex = cur.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);if (displayNameIndex != -1) {String displayName = cur.getString(displayNameIndex);if (!TextUtils.isEmpty(displayName)) {String namePart =StringUtil.getNamePart(displayName);String ext = StringUtil.pathExtension(displayName);if (!TextUtils.isEmpty(namePart) && !TextUtils.isEmpty(ext)) {name = displayName.trim();}}}}} catch (Exception ignore) {} finally {if (cur != null) {cur.close();}}return name;}private static String queryOtherColumn(Context context, Uri uri) {ContentResolver resolver = context.getContentResolver();Cursor cur = null;String name = null;try {cur = resolver.query(uri, null, null, null, null);if (cur != null && cur.getCount() == 1 && cur.moveToFirst()) {if (TextUtils.isEmpty(name)) {int fileNameIndex = cur.getColumnIndex("file_name");if (fileNameIndex != -1) {String fileName = cur.getString(fileNameIndex);if (!TextUtils.isEmpty(fileName)) {name = fileName;}}}if (TextUtils.isEmpty(name)) {int dataIndex = cur.getColumnIndex(MediaStore.MediaColumns.DATA);if (dataIndex != -1) {String dataPath = cur.getString(dataIndex);if (!TextUtils.isEmpty(dataPath)) {name = new File(dataPath).getName();}}}}} catch (Exception ignore) {} finally {if (cur != null) {cur.close();}}return name;}public static File getFileDir(Context context, String fileDir) {File file = context.getExternalFilesDir(fileDir);if (!file.exists()) {boolean result = file.mkdirs();if (!result) {throw new RuntimeException("create file dir fail");}}return file;}
}

相关文章:

WPS二次开发系列:如何使用WPS返回的FileUri

作者持续关注 WPS二次开发专题系列&#xff0c;持续为大家带来更多有价值的WPS开发技术细节&#xff0c;如果能够帮助到您&#xff0c;请帮忙来个一键三连&#xff0c;更多问题请联系我&#xff08;QQ:250325397&#xff09; 目录 什么是FileUri 在SDK中的使用场景 打开文档时…...

python删除一个文件夹所有文件

在Python中&#xff0c;可以使用os模块来删除一个文件夹中的所有文件&#xff0c;但保留文件夹本身。以下是一个简单的例子&#xff1a; import osdef delete_files_in_folder(folder_path):for filename in os.listdir(folder_path):file_path os.path.join(folder_path, fi…...

overflow:hidden对解决外边距塌陷的个人理解

外边距塌陷&#xff1a; 子元素的上外边距大于父元素的上外边距&#xff0c;导致边距折叠&#xff0c;取两者之间最大值&#xff0c;即子元素外边距&#xff0c;导致父元素上外边距失效。 解决办法&#xff1a;在父元素样式添加overflow:hidden;或者border:1px solid black;(不…...

【linux软件基础知识】- 文件的概念:Linux 中的文件

Linux 中的文件 在 Linux 中,文件是存储在存储设备(例如硬盘驱动器或固态驱动器)上的数据项的集合。 文件被组织为字节序列,并由文件系统中的唯一名称来标识。 以下是 Linux 中文件的一些关键特征: 字节序列:Linux 中的文件被视为字节序列。 每个字节可以表示一个字符…...

Context capture/Pix4Dmapper/AutoCAD/CASS/EPS软件的安装流程与使用方法;土方量计算;无人机摄影测量数据处理

目录 专题一 无人机摄影测量技术应用现状及其发展 专题二 基本原理和关键技术讲解 专题三 无人机影像外业数据获取 专题四 数据处理环境建立与软件熟悉 专题五 GNSS数据土方量计算 专题六 基于无人机影像数据的正射影像制作 专题七 基于无人机影像数据的三维模型制作 专…...

算法系列之堆排序实践哪家强

1.概念 堆排序是一种树形选择排序&#xff0c;是对简单选择排序的有效改进和优化。 堆(heap)&#xff0c;这里所说的堆是数据结构中的堆&#xff08;对应于算法&#xff09;&#xff0c;而不是内存模型中的堆&#xff08;数据存储形式&#xff0c;还比如&#xff1a;栈&#…...

01-win10安装Qt5

Qt5安装教程 下载Qt5官网下载(下载很慢)镜像网站下载(有些版本没有资源)迅雷下载(推荐)百度网盘下载(推荐)安装Qt5下载Qt5 官网下载(下载很慢) 【注意】:官网下载非常慢,没有镜像下载时常20+ Qt 官网有一个专门的资源下载网站,所有的开发环境和相关工具都可以从这…...

mybatis使用及配置相关,仅做个人记录

在spring-boot项目中mybatis的配置文件在yml文件中&#xff0c;并没有mybatisconfig.xml文件 yml文件中配置&#xff1a;&#xff08;来源&#xff1a;https://blog.51cto.com/u_16213723/8747999&#xff09; mybatis:# XML文件路径&#xff0c;可配置多个&#xff0c;逗号分…...

【STM32 |新建一个工程】基于标准库(库函数)新建工程

目录 STM32开发方式 库函数文件夹 建工程步骤 库函数工程建立 建立工程总结 STM32开发方式 目前stm32的开发方式主要有基于寄存器的方式、基于标准库的方式&#xff08;库函数的方式&#xff09;、基于HAL库的方式基于库函数的方式是使用ST官方提供的封装好的函数&…...

C#利用ClearScript执行Javascript脚本

1&#xff0c;新建.netframework winform工程 2&#xff0c;打开nuget程序包管理界面&#xff0c;安装Microsoft.ClearScript.V8&#xff0c;Microsoft.ClearScript.V8.Native.win-x64. 3,编写Javascript脚本,另存为demo.js function testFunc(t) {return t "&#xf…...

住宅ip与数据中心ip代理的区别是什么

代理通常意味着“替代”。它是用户设备和目标服务器之间的中介&#xff0c;允许在不同的IP地址下上网。代理ip根据来源分类可分住宅ip与数据中心ip&#xff0c;二者之间区别是什么呢&#xff1f; 住宅ip是由互联网服务提供商(ISP)提供给家庭的IP地址。出于这个原因&#xff0c…...

【计算机网络】数据链路层的功能

数据链路层的基本功能&#xff1a; 封装成帧透明传输差错检测 数据链路层使用的信道主要有两种 点对点信道——PPP协议广播信道——CSMA/CD协议(有线局域网)、CSMA/CA协议(无线局域网) 数据链路层所处的地位 从图中可以看出&#xff0c;数据从主机H1送到主机H2需要在路径中…...

信号线电路串联电阻

简介 两芯片端串联一个电阻&#xff0c;在靠近发送端或接收端。 一般串联的是0Ω, 22Ω, 33Ω的电阻&#xff0c;也可能更大。 目的 1.解决信号反射问题&#xff0c;吸收反射。 问题如下&#xff1a; pcb单端阻抗过大&#xff0c;而接收端是cmos输入&#xff0c;使得接收端…...

手机App防沉迷系统-算法

import java.util.*; public class Main{public static void main(String[] args){Scanner innew Scanner(System.in);int nInteger.parseInt(in.nextLine());//已注册app列表List<Log> listnew ArrayList<>();for(int k0;k<n;k){String[] strin.nextLine().spl…...

day3_prefixSum

一、前缀和技巧 重点 前缀和技巧适用于快速、频繁地计算一个索引区间内的元素之和 个人理解&#xff1b;预计算&#xff0c;空间换时间 1.(一维数组的前缀和)303区域和检索-数组不可变 获取闭区间值 [left,right] -> preSum[right 1] - preSum[left],其中preSum[right…...

Redis过期删除策略和内存淘汰策略有什么区别?

Redis过期删除策略和内存淘汰策略有什么区别&#xff1f; 前言过期删除策略如何设置过期时间&#xff1f;如何判定 key 已过期了&#xff1f;过期删除策略有哪些&#xff1f;Redis 过期删除策略是什么&#xff1f; 内存淘汰策略如何设置 Redis 最大运行内存&#xff1f;Redis 内…...

【计算机网络】物理层传输介质 习题3

双绞线是用两根绝缘导线绞合而成的&#xff0c;绞合的目的是( )。 A.减少干扰 B.提高传输速度 C.增大传输距离 D.增大抗拉强度 在电缆中采用屏蔽技术带来的好处主要是( ) A.减少信号衰减 B. 减少电磁干扰辐射 C.减少物理损坏 D. 减少电缆的阻抗 利用一根同轴电缆互连主机构成…...

智能座舱语音助手产品方案

一、用户调研与痛点分析 1.目标用户分析 用户画像 性别女性年龄50地域2-3线城市职业退休或退居二线教育中专、 大专、 本科财务家庭财务管理者爱好享受生活、 照顾家庭标签有闲有小钱二、产品定位与卖点提炼 购车目的 愉悦自我&#xff0c; 专属于自己的座驾&#xff1a; 家…...

经典面试题之滑动窗口专题

class Solution { public:int minSubArrayLen(int target, vector<int>& nums) {// 长度最小的子数组 // 大于等于 targetint min_len INT32_MAX;// 总和int sum 0;int start 0; // 起点for(int i 0; i< nums.size(); i) {sum nums[i];while(sum > targe…...

网络编程入门之UDP编程

欢迎各位帅哥美女来捧场&#xff0c;本文是介绍UDP网络编程。在这里&#xff0c;你会见到最详细的教程&#xff1b;细致到每一行代码&#xff0c;每一个api的由来和使用它的目的等。 目录 1.UDP相关API 1.1.两个类 1.2.两个类中的方法 2.UDP编程 2.1.大体框架 2.2.内容构…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...

DingDing机器人群消息推送

文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人&#xff0c;点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置&#xff0c;详见说明文档 成功后&#xff0c;记录Webhook 2 API文档说明 点击设置说明 查看自…...

破解路内监管盲区:免布线低位视频桩重塑停车管理新标准

城市路内停车管理常因行道树遮挡、高位设备盲区等问题&#xff0c;导致车牌识别率低、逃费率高&#xff0c;传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法&#xff0c;正成为破局关键。该设备安装于车位侧方0.5-0.7米高度&#xff0c;直接规避树枝遮…...

tomcat指定使用的jdk版本

说明 有时候需要对tomcat配置指定的jdk版本号&#xff0c;此时&#xff0c;我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...

嵌入式面试常问问题

以下内容面向嵌入式/系统方向的初学者与面试备考者,全面梳理了以下几大板块,并在每个板块末尾列出常见的面试问答思路,帮助你既能夯实基础,又能应对面试挑战。 一、TCP/IP 协议 1.1 TCP/IP 五层模型概述 链路层(Link Layer) 包括网卡驱动、以太网、Wi‑Fi、PPP 等。负责…...

使用ch340继电器完成随机断电测试

前言 如图所示是市面上常见的OTA压测继电器&#xff0c;通过ch340串口模块完成对继电器的分路控制&#xff0c;这里我编写了一个脚本方便对4路继电器的控制&#xff0c;可以设置开启时间&#xff0c;关闭时间&#xff0c;复位等功能 软件界面 在设备管理器查看串口号后&…...

联邦学习带宽资源分配

带宽资源分配是指在网络中如何合理分配有限的带宽资源&#xff0c;以满足各个通信任务和用户的需求&#xff0c;尤其是在多用户共享带宽的情况下&#xff0c;如何确保各个设备或用户的通信需求得到高效且公平的满足。带宽是网络中的一个重要资源&#xff0c;通常指的是单位时间…...

关于疲劳分析的各种方法

疲劳寿命预测方法很多。按疲劳裂纹形成寿命预测的基本假定和控制参数&#xff0c;可分为名义应力法、局部应力一应变法、能量法、场强法等。 1名义应力法 名义应力法是以结构的名义应力为试验和寿命估算的基础&#xff0c;采用雨流法取出一个个相互独立、互不相关的应力循环&…...