从Google Drive URI获取路径

15

我正在使用Android文件选择器从应用程序存储中选择文件(图像、视频、文档)。我有一个名为“getPath”的函数,它可以从URI获取路径。我在处理来自图库图像或下载文档时没有问题。但是当我从Google Drive中选择文件时,我无法获得路径。这是Google Drive URI “content://com.google.android.apps.docs.storage/document/acc%3D25%3Bdoc%3D12” 你能帮助我吗?

这也是我的“getPath”函数。

public static String getPath(final Context context, final Uri uri) {

    // check here to KITKAT or new version
    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {

        // ExternalStorageProvider
        if (isExternalStorageDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            if ("primary".equalsIgnoreCase(type)) {
                return Environment.getExternalStorageDirectory() + "/"
                        + split[1];
            }
        }
        // DownloadsProvider
        else if (isDownloadsDocument(uri)) {

            final String id = DocumentsContract.getDocumentId(uri);
            final Uri contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"),
                    Long.valueOf(id));

            return getDataColumn(context, contentUri, null, null);
        }
        // MediaProvider
        else if (isMediaDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            Uri contentUri = null;
            if ("image".equals(type)) {
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            } else if ("video".equals(type)) {
                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            } else if ("audio".equals(type)) {
                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            }

            final String selection = "_id=?";
            final String[] selectionArgs = new String[] { split[1] };

            return getDataColumn(context, contentUri, selection,
                    selectionArgs);
        }
        else if(isGoogleDriveUri(uri)){
                //Get google drive path here

        }
    }
    // MediaStore (and general)
    else if ("content".equalsIgnoreCase(uri.getScheme())) {

        // Return the remote address
        if (isGooglePhotosUri(uri))
            return uri.getLastPathSegment();

        return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }

    return nopath;
}

public static String iStreamToString(InputStream is1)
{
    BufferedReader rd = new BufferedReader(new InputStreamReader(is1), 4096);
    String line;
    StringBuilder sb =  new StringBuilder();
    try {
        while ((line = rd.readLine()) != null) {
            sb.append(line);
        }
        rd.close();

    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    String contentOfMyInputStream = sb.toString();
    return contentOfMyInputStream;
}

你有解决方案吗? - Shweta Chauhan
有什么解决方案吗? - Ritu Nagpal
3个回答

17
Get file path from Google Drive we can easily access by Using File Provider by using following steps Code is working fine.

1) Add provider path in AndroidManifest file inside Applcation Tag.
<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name="com.satya.filemangerdemo.activity.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>


2) provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <cache-path
        name="my_cache"
        path="." />
    <cache-path
        name="cache"
        path="." />
    <external-cache-path
        name="external_cache"
        path="." />
    <files-path
        name="files"
        path="." />
</paths>

3)FileUtils.java

public class FileUtils {
    private static Uri contentUri = null;
 @SuppressLint("NewApi")
    public static String getPath(final Context context, final Uri uri) {
        // check here to KITKAT or new version
        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri))
          {
            / MediaProvider
             if (isMediaDocument(uri)) {
                 if (isGoogleDriveUri(uri)) {
                return getDriveFilePath(uri, context);
            }


          }
      }

4) isGoogleDriveUri method 

  private static boolean isGoogleDriveUri(Uri uri) {
        return "com.google.android.apps.docs.storage".equals(uri.getAuthority()) || "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority());
    }

5)getDriveFilePath method 
 private static String getDriveFilePath(Uri uri, Context context) {
        Uri returnUri = uri;
        Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null);
        /*
         * Get the column indexes of the data in the Cursor,
         *     * move to the first row in the Cursor, get the data,
         *     * and display it.
         * */
        int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
        int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
        returnCursor.moveToFirst();
        String name = (returnCursor.getString(nameIndex));
        String size = (Long.toString(returnCursor.getLong(sizeIndex)));
        File file = new File(context.getCacheDir(), name);
        try {
            InputStream inputStream = context.getContentResolver().openInputStream(uri);
            FileOutputStream outputStream = new FileOutputStream(file);
            int read = 0;
            int maxBufferSize = 1 * 1024 * 1024;
            int bytesAvailable = inputStream.available();

            //int bufferSize = 1024;
            int bufferSize = Math.min(bytesAvailable, maxBufferSize);

            final byte[] buffers = new byte[bufferSize];
            while ((read = inputStream.read(buffers)) != -1) {
                outputStream.write(buffers, 0, read);
            }
            Log.e("File Size", "Size " + file.length());
            inputStream.close();
            outputStream.close();
            Log.e("File Path", "Path " + file.getPath());
            Log.e("File Size", "Size " + file.length());
        } catch (Exception e) {
            Log.e("Exception", e.getMessage());
        }
        return file.getPath();
    }

1
以上代碼有效,我們可以從Google Drive獲取文件路徑,添加權限清單 <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.STORAGE" /> - Satyawan Hajare
我不明白你为什么说“通过使用文件提供者”?你并没有使用“文件提供者”。 - ClassA
这非常有用。谢谢你。我有一个需求,需要从Google Drive中一次选择多个图像。这可行吗?如果可以,如何实现?提前感谢。 - Abhijeet
对于多选媒体文件,我们需要在设置 Intent 时添加属性 intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)。 参考代码如下: 'Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); startActivityForResult(intent, 1);' 在 onActivityForResult(..) 方法中,使用 ClipData 获取所有选定图像的 URI。 - Abhijeet

13

我对画廊图片或下载文件没有问题。

但是在许多设备上,您会遇到问题。

但是当我从Google Drive中选择文件时,我无法获取路径。

没有路径。ACTION_GET_CONTENT不允许用户选择文件。它允许用户选择内容片段。该内容可能是本地文件。该内容也可能是:

  • 某个位于网络文件服务器上的东西 (链接1)
  • 某些东西位于云存储服务中,例如谷歌云端硬盘,因此目前并不在设备上
  • 某些东西位于加密容器
  • 某些东西确实是一个文件,但不是您可以直接访问的文件,例如来自其他应用程序内部存储的文件
  • 等等

您有两个主要选项。如果您只想获取文件,则可以使用第三方文件选择器库替换您问题中的所有代码。

或者,如果您仍想使用ACTION_GET_CONTENTACTION_OPEN_DOCUMENT,您可以在onActivityResult()中获取从data.getData()得到的Uri并对其进行两个操作:

  • 首先,使用DocumentFile.fromSingleUri()来获取指向该UriDocumentFile对象。您可以调用DocumentFile上的getName()来获取内容的“显示名称”,这应该是用户能够识别的名称。

  • 然后,使用ContentResolveropenInputStream()来访问内容本身,类似于您如何使用FileInputStream来访问文件中的字节。


1
获取输入流后该怎么办? - Shweta Chauhan
3
读取字节。然后对这些字节进行某些操作。具体的“某些操作”取决于内容以及您的应用程序功能是什么。 - CommonsWare
1
未来的读者,https://developer.android.com/guide/topics/providers/document-provider - olfek

6
我也遇到了同样的问题,发现当我们从Google Drive选择图片时,其URI如下:
com.google.android.apps.docs.storage

由于文件不在我们的设备上,因此我们无法直接获取文件路径。所以我们首先将文件下载到特定目标,然后我们可以使用该路径来完成我们的工作。以下是相应的代码:

FileOutputStream fos = null;
    try {
         fos = new FileOutputStream(getDestinationFilePath());
         try (BufferedOutputStream out = new BufferedOutputStream(fos); 
         InputStream in = mContext.getContentResolver().openInputStream(uri)) 
           {
            byte[] buffer = new byte[8192];
            int len = 0;

            while ((len = in.read(buffer)) >= 0) {
                  out.write(buffer, 0, len);
                 }

             out.flush();
            } finally {
                       fos.getFD().sync();
                      }

                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

            File file = new File(destinationFilePath);
            if (Integer.parseInt(String.valueOf(file.length() / 1024)) > 1024) {
            InputStream imageStream = null;



            try {
                 imageStream = mContext.getContentResolver().openInputStream(uri);
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        }

现在你的文件已经保存到所需的目标路径,你可以使用它了。


请问您能解释一下吗?getDestinationFilePath()是什么? - Ritu Nagpal
getDestinationFilePath() 是你将写入文件的新路径。 - Ankit Shukla

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接