android 中 webview 上传图片后,图片文件失去后缀的问题

在webview中选择图片一般是设置WebChromeClient,然后重写openFileChooser方法,最后在onActivityResult中得到图片Uri

问题来了,这个Uri一般都没有文件后缀(.jpg.png),像下面这样

filecontent://media/external/images/media/68987

将图片上传到服务器后,保存在服务器的文件名字就是68987了,文件没有后缀,将后缀改为.jpg之后,图片是正确的,,,

遇到这个问题排查了一天

解决方式

根据 Uri 获取文件的真实路径, 然后再把真实路径转成 Uri. 这时候的 Uri 就是带文件真实名和后缀的.
// 根据 Uri 获取文件真实路径的代码:
https://gist.github.com/atearsan/1f0846e6dcdb3d8f3d1d

// 真实路径转成 Uri 的代码: Uri.fromFile(new File(filePath));
// Uri 打印结果: file:///storage/emulated/0/DCIM/Camera/IMG_20151006_193049.jpg


// 根据 Uri 获取文件真实路径的代码





package com.atearsan.util;

import android.annotation.TargetApi;
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;


// Code taken from: http://stackoverflow.com/questions/20067508/get-real-path-from-uri-android-kitkat-new-storage-access-framework
public class MediaUtility {
    /**
     * 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
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    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 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());
    }
}

 

 

以下做一下科普:

 

一、URI

通用资源标志符(Universal Resource Identifier, 简称"URI")。

Uri代表要操作的数据,Android上可用的每种资源 - 图像、视频片段等都可以用Uri来表示。

URI一般由三部分组成:

访问资源的命名机制。 

存放资源的主机名。 

资源自身的名称,由路径表示。 

Android的Uri由以下三部分组成: "content://"、数据的路径、标示ID(可选)

举些例子,如: 

所有联系人的Uri: content://contacts/people

某个联系人的Uri: content://contacts/people/5

所有图片Uri: content://media/external

某个图片的Uri:content://media/external/images/media/4

 

二、内部保存

首先我们来看一下android是如何管理多媒体文件(音频、视频、图片)的信息。通过DDMS,我们在/data/data/com.android.providers.media下找到数据库文件

打开external.db文件进一步查看:在media表格下,可以看到文件路径(_data)和Uri的标示ID(_id)的对应关系。

三、相互转换

 1.从URI获得文件路径

 

 1     string  myImageUrl = "content://media/external/images/media/***";
 2     Uri uri = Uri.parse(myImageUrl);
 3     
 4 
 5     String[] proj = { MediaStore.Images.Media.DATA };   
 6     Cursor actualimagecursor = this.ctx.managedQuery(uri,proj,null,null,null);  
 7     int actual_image_column_index = actualimagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);   
 8     actualimagecursor.moveToFirst();   
 9     
10 
11     String img_path = actualimagecursor.getString(actual_image_column_index);  
12     File file = new File(img_path);
13     Uri fileUri = Uri.fromFile(file);

 

 

 

2.由文件路径得到URI

 1             Uri mUri = Uri.parse("content://media/external/images/media"); 
 2             Uri mImageUri = null;
 3 
 4             Cursor cursor = managedQuery(
 5                     MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null,
 6                     null, MediaStore.Images.Media.DEFAULT_SORT_ORDER);
 7             cursor.moveToFirst();
 8 
 9             while (!cursor.isAfterLast()) {
10                 String data = cursor.getString(cursor
11                         .getColumnIndex(MediaStore.MediaColumns.DATA));
12                 if (picPath.equals(data)) {
13                     int ringtoneID = cursor.getInt(cursor
14                             .getColumnIndex(MediaStore.MediaColumns._ID));
15                     mImageUri = Uri.withAppendedPath(mUri, ""
16                             + ringtoneID);
17                     break;
18                 }
19                 cursor.moveToNext();
20             }

 

 


版权声明:本文为ffzhihua原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。