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

什么是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() {
        @Override
        public 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() {
                @Override
                public 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;
    }
}