在Android Studio中使用文件选择器时出现错误,位于下载文件夹中。

huangapple go评论71阅读模式
英文:

Error in Android Studio with File Chooser in Download Folder

问题

My Problem

我目前正在使用Android Studio开发Android应用程序。
要选择文件,用户点击按钮,然后资源管理器打开,用户可以选择文件。
为了执行此过程,我使用了一个名为FileChooser.Java的辅助类。只要用户不从“下载”文件夹中选择文件,整个系统就运行得非常好。

如果用户尝试从“下载”文件夹中选择文件,将会发生以下情况:

NumberFormatException: for input string: "msf:80123"

FileChooser返回的URL:
content://com.android.providers.downloads.documents/document/msf:80662

这是因为辅助类期望得到长整型数据。即使删除msf:并将ID添加到末尾,也不起作用。似乎Android会将“下载”文件夹中的所有内容都加上这个msf:标签。

我正在寻找一个有效的解决方案,可以从“下载”文件夹中调用文件。

The Call

Uri selectedFile = data.getData();
InputStream dataStream = new FileInputStream(FileChooser.getPath(getContext(), selectedFile));

在FileChooser类中的异常调用

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

辅助类FileChooser

package com.example.dsvconverter.helper;

import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;

public class FileChooser {

    /**
     * 从Uri获取文件路径。这将获取存储访问框架文档的路径,以及MediaStore和其他基于文件的ContentProvider的_data字段。
     *
     * @param context 上下文。
     * @param uri 要查询的Uri。
     * @return _data列的值,通常是文件路径。
     */
    public static String getPath(final Context context, final Uri uri) {
        // ...(函数实现已省略)
    }

    // ...(其他函数实现已省略)
}

更新:
@blackapps提供的代码有效,但现在我遇到了一个新问题。

Blackapps的代码:

InputStream dataStream = getContentResolver().openInputStream(data.getData());

这个解决方案有效,但现在有些不同。我正在制作一个PGP工具,这个InputStream包含从所选文件中读取的公钥。如果我按照你的方式创建InputStream,那么PGP库将不再将其识别为公钥。然而,如果我将其打印到控制台,它看起来是正确的,与原始内容完全相同。

问题:

为什么@Blackapps的解决方案有效,但是即使打印出来的字符串完全相同,库也不再将其识别为公钥呢?

英文:

My Problem

I am currently developing an Android application with Android Studio.
To choose a file the user clicks on a button, the explorer opens and he can select a file.
For this process I used a helper class called FileChooser.Java. This whole system functions very well as long as the user doesn't select a file from the Download folder.

If the users tries to select a file from the Download folder this happens:

NumberFormatException: for input string: "msf:80123"

The URL the FileChooser returns:
content://com.android.providers.downloads.documents/document/msf:80662

This is because the helper class expects a long type. Removing the msf: and adding the ID to the end doesn't work either. It seems like Android adds this msf: tag to everything in the Download folder.

I am looking for a valid solution to call files from the Download folder too.

The Call

Uri selectedFile = data.getData();
InputStream dataStream = new FileInputStream(FileChooser.getPath(getContext(), selectedFile));

Exception Call in FileChooser Class

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

Helper Class FileChooser

package com.example.dsvconverter.helper;

import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;


public class FileChooser {

    /**
     * Get a file path from a Uri. This will get the the path for Storage Access
     * Framework Documents, as well as the _data field for the MediaStore and
     * other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @author paulburke
     */
    public static String getPath(final Context context, final Uri uri) {

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

                // TODO handle non-primary volumes
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {

                final String id = DocumentsContract.getDocumentId(uri);
                final String[] split = id.split(":");
                final String type = split[0];

                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);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @param selection (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    public static String getDataColumn(Context context, Uri uri, String selection,
                                       String[] selectionArgs) {

        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {
                column
        };

        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }


    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }
}

Update:
The code provided by @blackapps worked, however now I have a new problem.

Blackapps Code:

InputStream dataStream = getContentResolver().openInputStream(data.getData());

This solution works, however something is different now. I am making a PGP Tool and this InputStream contains a Public Key read from the file selected. The PGP Library doesn't recognize it as a Public Key anymore if I create the InputStream with your way. However if I print it into the console, it looks correct and same.

Question:

Why does @Blackapps solution work, but the library doesn't recognize it as a Public Key anymore, even if the printed string is 1:1 same?

答案1

得分: 5

在onActivityResult()中:

InputStream dataStream = getContentResolver().openInputStream(data.getData());

抛弃那个getPath()函数

这适用于所有的URI
英文:

In onActivityResult() :

InputStream dataStream = getContentResolver().openInputStream(data.getData());

Throw away that getPath() function.

This works for all uries.

答案2

得分: 5

以下是翻译好的代码部分:

唯一的问题是获取下载目录的路径或msf和NumberFormat异常请尝试这个它对我来说正常工作了

package com.example.bookingmelbourne;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class FileUtils {
    private static final String TAG = "FileUtils";
    @WorkerThread
    @Nullable
    public static String getReadablePathFromUri(Context context, Uri uri) {

        String path = null;
        if ("file".equalsIgnoreCase(uri.getScheme())) {
            path = uri.getPath();
        }

        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
            path = getPath(context, uri);
        }

        if (TextUtils.isEmpty(path)) {
            return path;
        }

        Log.d(TAG, "从URI获取路径:" + path);
        if (!isReadablePath(path)) {
            int index = path.lastIndexOf("/");
            String name = path.substring(index + 1);
            String dstPath = context.getCacheDir().getAbsolutePath() + File.separator + name;
            if (copyFile(context, uri, dstPath)) {
                path = dstPath;
                Log.d(TAG, "复制文件成功:" + path);
            } else {
                Log.d(TAG, "复制文件失败!");
            }
        }
        return path;
    }

    public static String getPath(final Context context, final Uri uri) {
        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                Log.d("External Storage", docId);
                final String[] split = docId.split(":");
                final String type = split[0];

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

                String dstPath = context.getCacheDir().getAbsolutePath() + File.separator + getFileName(context, uri);

                if (copyFile(context, uri, dstPath)) {
                    Log.d(TAG, "复制文件成功:" + dstPath);
                    return dstPath;
                } else {
                    Log.d(TAG, "复制文件失败!");
                }

            } 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 ("content".equalsIgnoreCase(uri.getScheme())) {
            return getDataColumn(context, uri, null, null);
        } else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }
        return null;
    }

    public static String getFileName(Context context, Uri uri) {

        Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
        int nameindex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
        cursor.moveToFirst();

        return cursor.getString(nameindex);
    }

    private static String getDataColumn(Context context, Uri uri, String selection,
                                        String[] selectionArgs) {
        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {column};

        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }

    private static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    private static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    private static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }

    private static boolean isReadablePath(@Nullable String path) {
        if (TextUtils.isEmpty(path)) {
            return false;
        }
        boolean isLocalPath;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            if (!TextUtils.isEmpty(path)) {
                File localFile = new File(path);
                isLocalPath = localFile.exists() && localFile.canRead();
            } else {
                isLocalPath = false;
            }
        } else {
            isLocalPath = path.startsWith(File.separator);
        }
        return isLocalPath;
    }

    private static boolean copyFile(Context context, Uri uri, String dstPath) {
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = context.getContentResolver().openInputStream(uri);
            outputStream = new FileOutputStream(dstPath);

            byte[] buff = new byte[100 * 1024];
            int len;
            while ((len = inputStream.read(buff)) != -1) {
                outputStream.write(buff, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return true;
    }

  }

之后,您可以调用此方法,传递上下文和URI对象:

String realPath = FileUtils.getReadablePathFromUri(context, uri);
英文:

The only problem is getting the path of the download directory or msf and NumberFormat exception, Try this one. It worked for me correctly

package com.example.bookingmelbourne;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class FileUtils {
private static final String TAG = "FileUtils";
@WorkerThread
@Nullable
public static String getReadablePathFromUri(Context context, Uri uri) {
String path = null;
if ("file".equalsIgnoreCase(uri.getScheme())) {
path = uri.getPath();
}
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
path = getPath(context, uri);
}
if (TextUtils.isEmpty(path)) {
return path;
}
Log.d(TAG, "get path from uri: " + path);
if (!isReadablePath(path)) {
int index = path.lastIndexOf("/");
String name = path.substring(index + 1);
String dstPath = context.getCacheDir().getAbsolutePath() + File.separator + name;
if (copyFile(context, uri, dstPath)) {
path = dstPath;
Log.d(TAG, "copy file success: " + path);
} else {
Log.d(TAG, "copy file fail!");
} 
}
return path;
}
public static String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
Log.d("External Storage", docId);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
} else if (isDownloadsDocument(uri)) {
String dstPath = context.getCacheDir().getAbsolutePath() + File.separator + getFileName(context,uri);
if (copyFile(context, uri, dstPath)) {
Log.d(TAG, "copy file success: " + dstPath);
return dstPath;
} else {
Log.d(TAG, "copy file fail!");
}
} 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 ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(context, uri, null, null);
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
public static String getFileName(Context context, Uri uri) {
Cursor cursor = context.getContentResolver().query(uri,null,null,null,null);
int nameindex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
cursor.moveToFirst();
return  cursor.getString(nameindex);
}
private static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {column};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
private static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
private static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
private static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
private static boolean isReadablePath(@Nullable String path) {
if (TextUtils.isEmpty(path)) {
return false;
}
boolean isLocalPath;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (!TextUtils.isEmpty(path)) {
File localFile = new File(path);
isLocalPath = localFile.exists() && localFile.canRead();
} else {
isLocalPath = false;
}
} else {
isLocalPath = path.startsWith(File.separator);
}
return isLocalPath;
}
private static boolean copyFile(Context context, Uri uri, String dstPath) {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = context.getContentResolver().openInputStream(uri);
outputStream = new FileOutputStream(dstPath);
byte[] buff = new byte[100 * 1024];
int len;
while ((len = inputStream.read(buff)) != -1) {
outputStream.write(buff, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return true;
}
}

After that, you can call this method passing context and uri objects

 String realPath = FileUtils.getReadablePathFromUri(context,uri)

huangapple
  • 本文由 发表于 2020年9月11日 20:26:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/63847083.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定