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;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/6912.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows如何安装hadoop

Hadoop是一个开源的分布式计算平台&#xff0c;旨在处理大规模数据的存储和处理。它提供了分布式文件系统&#xff08;HDFS&#xff09;和分布式计算框架&#xff08;MapReduce&#xff09;&#xff0c;使得用户能够在大规模集群上存储和处理数据。Hadoop最初由Apache软件基金会…

15、Python:循环控制语句

在编程中&#xff0c;循环控制语句是实现代码重复执行的基本结构。Python 提供了多种循环控制结构&#xff0c;以适应不同的编程场景。本文将详细介绍 Python 中的 for 循环和 while 循环&#xff0c;以及如何使用 break 和 continue 语句来控制循环流程。 for 循环 for 循环…

spring boot test 设置环境变量

在 Spring Boot 中&#xff0c;可以通过在测试类上添加 TestPropertySource 注解来设置测试环境变量。该注解可以指定一个或多个 properties 文件&#xff0c;或者一个或多个 key-value 形式的环境变量。 例如&#xff0c;如果您有一个名为 application-test.properties 的测试…

cmake进阶:定义函数的使用方法

一. 简介 前面已经将 cmake 中常用的命令 command、变量 variable 都给大家进行了详细介绍&#xff0c;通过前面的学习&#xff0c;相信大家已经掌握了 cmake 工具的基本使用方法&#xff1b; 接下来我们再进一步学习 cmake&#xff0c;本文开始学习 cmake中定义函数。 二. …

TypeScript学习日志-第十九天(namespace命名空间)

namespace命名空间 一、基本用法 namespace 所有的变量以及方法必须要导出才能访问&#xff0c;如图&#xff1a; 二、 嵌套 namespace 可以进行嵌套使用&#xff0c;如图&#xff1a; 它也必须需要导出才能访问 三、合并 当我们出现两个同名的 namespace 它就会合并这两…

EFDC模型安装及建模方法;在排污口论证、水质模拟、地表水环评、地表水水源地划分、水环境容量计算等领域中的应用

目录 专题一 EFDC软件安装 专题二 EFDC模型讲解 专题三 一维河流模拟实操 专题四 建模前处理 专题五 EFDC网格剖分介绍 专题六 EFDC二维湖库水动力模拟/非保守染色剂模拟 专题七 EFDC水质模型参数及原理介绍 专题八 EFDC一、二、三维湖库水质模拟 专题九 基于EFDC的地…

nodejs的ws+vue3编写聊天室的demo

nodejs编写ws服务是非常简单高效的&#xff0c;nodejs有众多的实现ws的库&#xff0c;如ws,SocketIO等&#xff0c;nodejs的事件线程是单线程的&#xff0c;所以不要在事件线程内做阻塞性的操作&#xff0c;耗时的操作交给工作线程或者子进程操作。 我使用nodejsvue3实现了写了…

408数据结构-二叉树的遍历 自学知识点整理

前置知识&#xff1a;二叉树的概念、性质与存储结构 二叉树的遍历 二叉树的遍历是指按某条搜索路径访问树中每个结点&#xff0c;使得每个结点均被访问一次&#xff0c;而且仅被访问一次。 二叉树的递归特性: ①要么是棵空二叉树&#xff1b; ②要么就是由“根节点左子树右子树…

【NOI】C++程序结构入门之分支结构二

文章目录 前言一、逻辑运算符1.导入2.逻辑与&#xff08;&&&#xff09;3.逻辑或&#xff08;||&#xff09;4.逻辑非&#xff08;!&#xff09; 二、例题讲解问题&#xff1a;1656. 是两位的偶数吗问题&#xff1a;1658. 游乐设施问题&#xff1a;1659. 是否含有数字5…

Linux下的Git指令操作

1.安装git sudo apt-get install git 2.本地拉取已有仓库项目(红色部分请替换) git clone https://github.com/test/test.git 3. 上传本地新增内容&#xff08;文本或文件夹&#xff09; git add XXX 4.添加新增内容说明 git commit -m XXXX 5. 将本地仓库同步修改到远程仓库 …

AI绘画:Stable Diffusion 拒绝一眼塑料味的AI质感,超写实人物图片如何制作?简单几步教会你!

今天给大家介绍一款能够对生成的人像进行皮肤调节的 lora。 上面两幅图片的生成参数一样&#xff0c;尺寸也一样&#xff0c;但右边一幅图片相较于左面图片的画面质感&#xff0c;特别是人像皮肤的质感上有很大的提升&#xff0c;看上去更加细腻有层感。 这就是我们今天要介绍…

条款5:对定制的“类型转换函数”保持警觉

C允许编译器在不同类型之间执行隐式转换&#xff08;implicit conversions&#xff09;。 继承了C的伟大传统&#xff0c;这个语言允许默默地将char转换为 int&#xff0c;将 short 转换为 doublea这便是为什么你可以将一个short 交给一个“期望获得double”的函数而仍能成功的…

linux下的调试工具gdb的详细使用介绍

在之前学习中我们使用的通常是集各种功能于一体的编译器&#xff0c;例如VS stdio&#xff0c;但是一个程序在编辑后还要进行编译&#xff0c;然后才能产生一个二进制的可执行文件&#xff0c;编辑和翻译工作都可以使用不同的软件进行&#xff0c;例如记事本就是一款编辑软件&a…

03.配置监控一台服务器主机

配置监控一台服务器主机 安装zabbix-agent rpm -ivh https://mirror.tuna.tsinghua.edu.cn/zabbix/zabbix/4.0/rhel/7/x86_64/zabbix-agent-4.0.11-1.el7.x86_64.rpm配置zabbix-agent,配置的IP地址是zabbix-server的地址&#xff0c;因为要监控这台主机 vim /etc/zabbix/zab…

android开发环境搭建步骤

搭建Android开发环境需要完成以下几个步骤&#xff1a; 下载Android Studio&#xff1a;访问Android官方网站下载最新版本的Android Studio安装包。安装Android Studio&#xff1a;运行下载的安装程序&#xff0c;按照提示完成安装过程。如果C盘空间足够&#xff0c;推荐安装在…

智能指针三剑客:shared_ptr的使用

目录 shared_ptr错误范例 其他补充 尺寸问题 移动语义 shared_ptr错误范例 裸指针&#xff1a; #include<iostream> using namespace std; void Test(shared_ptr<int> ps) {return; } int main() {int *pnew int(666);Test(p); //E0415 不存在从 "int *&…

免费开源线上线下交友社交圈子系统 小程序+APP+H5 可支持二开!

为什么要玩社交软件&#xff1a;互联网社交软件的独特优势 首先&#xff0c;社交软件为我们提供了一个便捷的沟通方式。在传统的交往方式中&#xff0c;人们需要面对面交流&#xff0c;这种方式在时间和空间上都受到限制。而社交软件打破了这些限制&#xff0c;无论我们身处何地…

既能自动仿写公众号爆文,还能批量帮你上架闲鱼商品,打造自己的数字员工,简直yyds

「想象一下&#xff0c;如果有一个机器人在你的计算机上24小时不间断地工作&#xff0c;会不会做梦都笑着」 一、RPA机器人是什么&#xff1f; RPA——机器人流程自动化&#xff0c;它可以帮助人们完成重复性的、繁琐的工作&#xff0c;比如数据输入、网页爬取、自动化流程等…

llama3 史上最强开源大模型,赶超GTP-4,逼宫OpenAI

2024年4月18日&#xff0c;Meta公司推出了开源大语言模型Llama系列的最新产品—Llama 3&#xff0c;包含了80亿参数的Llama 3 8B和700亿参数的Llama 3 70B两个版本。Meta称其为“迄今为止最强的开源大模型”。 怪兽级性能 LLaMA3 提供了不同参数规模的版本&#xff0c;以适应…

你真的知道Show Master Status吗?

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 你真的知道Show Master Status吗&#xff1f; 前言输出字段展示file详解Position详解Binlog_Do_DBBinlog_Ignore_DBExecuted_Gtid_Set 前言 在数据库的世界里&#xff0c;每一个字段都像是一个谜团&a…