【问题标题】:IllegalArgumentException: column '_data' does not existIllegalArgumentException:列“_data”不存在
【发布时间】:2017-07-19 09:39:21
【问题描述】:

在 Nougat 中,此功能不起作用。

String path = getRealPathFromURI(this, getIntent().getParcelableExtra(Intent.EXTRA_STREAM));


public String getRealPathFromURI(Context context, Uri contentUri) {
    Cursor cursor = null;
    try {
        String[] proj = {MediaStore.Images.Media.DATA};
        cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
        if (cursor == null) return contentUri.getPath();
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        return cursor.getString(column_index);
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
}

崩溃日志:

java.lang.RuntimeException: Unable to start activity ComponentInfo{class path}: java.lang.IllegalArgumentException: column '_data' does not exist
   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2659)
   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2724)
   at android.app.ActivityThread.-wrap12(ActivityThread.java)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1473)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:154)
   at android.app.ActivityThread.main(ActivityThread.java:6123)
   at java.lang.reflect.Method.invoke(Method.java)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Caused by java.lang.IllegalArgumentException: column '_data' does not exist
   at android.database.AbstractCursor.getColumnIndexOrThrow(AbstractCursor.java:333)
   at android.database.CursorWrapper.getColumnIndexOrThrow(CursorWrapper.java:87)
   at com.package.SaveImageActivity.getRealPathFromURI()
   at com.package.SaveImageActivity.onCreate()
   at android.app.Activity.performCreate(Activity.java:6672)
   at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1140)
   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2612)
   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2724)
   at android.app.ActivityThread.-wrap12(ActivityThread.java)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1473)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:154)
   at android.app.ActivityThread.main(ActivityThread.java:6123)
   at java.lang.reflect.Method.invoke(Method.java)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)

这个功能在Android N之前的设备上可以正常工作。我看了文章file:// scheme is now not allowed to be attached with Intent on targetSdkVersion 24 (Android Nougat)。但找不到任何解决方案。所以请帮忙。

【问题讨论】:

  • 为什么要从 URI 中获取文件路径?通常,您应该尝试将 URI 作为流使用,因为并非所有 URI 都由具有路径的本地文件支持。如果您试图访问该文件以读取 exif 数据,那么您可以将其作为流打开并使用此支持库来读取 exif 数据。 android-developers.googleblog.com/2016/12/…
  • @startoftext 我知道这是一个迟到的问题,但是通过您的方法,有各种设备会显示图像的 ORIENTATION_UNDEFINED,它们可以是横向或纵向,具体取决于 API 版本。对于模拟器上的 API 26 (Android 8),它会旋转 90 度,而在 API 29 及更高版本(Android 9 及更高版本)上,它显示的是纵向图像,没有任何旋转。你有什么解决办法吗?

标签: android android-7.0-nougat android-7.1-nougat


【解决方案1】:

根据 Android R 存储框架指南进一步添加 Vidha 的答案,如果您不想将复制的文件永久保存到存储中:

    public static String getFilePathFromURI(Context context, Uri contentUri) {
    File folder;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        folder = new File (Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)+ "/"+ "yourAppFolderName" );
    } else {
        folder = new File(Environment.getExternalStorageDirectory() + "/"+ "yourAppFolderName");
    }
    // if you want to save the copied image temporarily for further process use .TEMP folder otherwise use your app folder where you want to save
    String TEMP_DIR_PATH = folder.getAbsolutePath() + "/.TEMP_CAMERA.xxx";
    //copy file and send new file path
    String fileName = getFilename();
    if (!TextUtils.isEmpty(fileName)) {
        File dir = new File(TEMP_DIR_PATH);
        dir.mkdirs();
        File copyFile = new File(dir, fileName);
        copy(context, contentUri, copyFile);
        return copyFile.getAbsolutePath();
    }
    return null;
}

public static void copy(Context context, Uri srcUri, File dstFile) {
    try {
        InputStream inputStream = context.getContentResolver().openInputStream(srcUri);
        if (inputStream == null) return;
        OutputStream outputStream = new FileOutputStream(dstFile);
        IOUtils.copyStream(inputStream, outputStream);
        inputStream.close();
        outputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public static String getFilename() {
    // if file is not to be saved, this format can be used otherwise current fileName from Vidha's answer can be used
    String ts = (new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US)).format(new Date());
    return ".TEMP_" + ts + ".xxx";
}

【讨论】:

    【解决方案2】:

    //以下代码在Android N中运行:

    private static String getFilePathForN(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.getFilesDir(), 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();
    }
    

    【讨论】:

    • 您应该添加一个解释,说明您的代码在做什么以及为什么它有效而相关代码没有。
    • IllegalArgumentException: column '_data' does not exist 这个问题发生在 Android Naugat 上面的方法问题已解决。
    • 唯一完美运行的灵魂。自 10 小时以来一直在苦苦挣扎
    • 如果文件很大,会花费很多时间。
    【解决方案3】:

    根据 CommonsWare 给出的答案,解决方案代码是:

    public static String getFilePathFromURI(Context context, Uri contentUri) {
        //copy file and send new file path 
        String fileName = getFileName(contentUri);
        if (!TextUtils.isEmpty(fileName)) {
            File copyFile = new File(TEMP_DIR_PATH + File.separator + fileName);
            copy(context, contentUri, copyFile);
            return copyFile.getAbsolutePath();
        }
        return null;
    }
    
    public static String getFileName(Uri uri) {
        if (uri == null) return null;
        String fileName = null;
        String path = uri.getPath();
        int cut = path.lastIndexOf('/');
        if (cut != -1) {
            fileName = path.substring(cut + 1);
        }
        return fileName;
    }
    
    public static void copy(Context context, Uri srcUri, File dstFile) {
        try {
            InputStream inputStream = context.getContentResolver().openInputStream(srcUri);
            if (inputStream == null) return;
            OutputStream outputStream = new FileOutputStream(dstFile);
            IOUtils.copyStream(inputStream, outputStream);
            inputStream.close();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

    希望对你有帮助。

    IOUtils.copy 的来源来自这个站点:https://www.developerfeed.com/copy-bytes-inputstream-outputstream-android/(可能需要更改一些异常,但它可以根据需要工作)

    【讨论】:

    • TEMP_DIR_PATH 和 IOUtils 是什么? @vidha
    • 对于TEMP_DIR_PAT,我使用了Environment.getExternalStorageDirectory().getPath() + "/YourAppName"
    • 看起来这段代码抛出了 FileNotException:stackoverflow.com/a/47350375/3763032
    • @mochadwi,代码绝对需要存储权限。
    • 我还实现了清单和运行时权限。这段代码可以无缝运行,谢谢@vidha 创建一个存在的文件时我的代码有问题,我直接删除了它而不是保留它。之后出现copy(in, out),当然是找不到源文件了。
    【解决方案4】:

    @Redturbo 我不能写评论,我写在这里

    IOUtils.copyStream(inputStream, outputStream);
    

    TEMP_DIR_PATH - 任何你的目录路径,类似这样的

     File rootDataDir = context.getFilesDir();
     File copyFile = new File( rootDataDir + File.separator + fileName + ".jpg");
    

    【讨论】:

    • 如果你能写评论你会写什么?
    【解决方案5】:

    此功能在Android N之前的设备上正常工作

    它适用于极少数Uri 值,可能没有结果(例如,对于由MediaStore 索引的不是本地文件的内容),并且可能没有可用的结果(例如,对于可移动存储)。

    所以请帮忙。

    使用ContentResolveropenInputStream()Uri 标识的内容上获得InputStream。理想情况下,只需直接使用该流,无论您要做什么。或者,在您控制的文件上使用 InputStream 和一些 FileOutputStream 来复制内容,然后使用该文件。

    【讨论】:

    • 如果我想要文件路径并在数据库中保存文件路径怎么办?
    • 如果我想要来自 Uri 的文件并显示图像怎么办?
    • @vidha:正如我在答案中所写,您需要将数据复制到您自己的文件,然后由您管理。
    • 我遇到了同样的问题,但不知道如何解决,请帮忙。 @Vidha 你能在这里提供你的代码吗?
    • @BajrangHudda 请在下面找到答案。
    猜你喜欢
    • 2013-09-22
    • 1970-01-01
    • 1970-01-01
    • 2020-08-31
    • 2018-09-12
    • 1970-01-01
    • 1970-01-01
    • 2016-04-08
    • 1970-01-01
    相关资源
    最近更新 更多