Android根據圖片Uri獲取圖片path絕對路徑的幾種方法【轉】

来源:https://www.cnblogs.com/blog4wei/archive/2018/09/03/9578405.html
-Advertisement-
Play Games

在Android 編程中經常會用到Uri轉化為文件路徑,如我們從相冊選擇圖片上傳至伺服器,一般上傳前需要對圖片進行壓縮,這時候就要用到圖片的絕對路徑。 下麵對我開發中uri轉path路徑遇到的問題進行總結,其中涉及到Android不同api下對於uri的處理,還有對於Google相冊圖片該如何獲取其 ...


在Android 編程中經常會用到Uri轉化為文件路徑,如我們從相冊選擇圖片上傳至伺服器,一般上傳前需要對圖片進行壓縮,這時候就要用到圖片的絕對路徑。 
下麵對我開發中uri轉path路徑遇到的問題進行總結,其中涉及到Android不同api下對於uri的處理,還有對於Google相冊圖片該如何獲取其圖片路徑。

1. 從相冊獲取圖片

我們從相冊獲取的圖片的代碼如下:

1 // 激活系統圖庫,選擇一張圖片
2 Intent intent = new Intent(Intent.ACTION_PICK);
3 intent.setType("image/*");
4 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
5 startActivityForResult(intent, Constants.PHOTO_REQUEST_GALLERY);

當然針對Android 6.0以上系統還需要獲取WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE操作手機sd卡許可權才行。 
然後再在onActivityResult中獲取到圖片的uri路徑:

 1 @Override
 2 public void onActivityResult(int requestCode, int resultCode, Intent data) {
 3         super.onActivityResult(requestCode, resultCode, data);
 4         if (resultCode == Activity.RESULT_OK) {
 5             switch (requestCode) {
 6                 case Constants.PHOTO_REQUEST_GALLERY:
 7                     Uri uri = data.getData();
 8                     break;
 9              }
10         }
11     }

Uri:通用資源標誌符(Universal Resource Identifier, 簡稱”URI”),Uri代表要操作的數據,Android上可用的每種資源(如圖像、視頻片段等)都可以用Uri來表示。 
Uri一般由三部分組成:訪問資源的命名機制。存放資源的主機名。資源自身的名稱,由路徑表示。

2.根據Uri獲取path路徑

2.1 Android 4.4以下獲取圖片路徑

 1 /**
 2      * 獲取小於api19時獲取相冊中圖片真正的uri
 3      * 對於路徑是:content://media/external/images/media/33517這種的,需要轉成/storage/emulated/0/DCIM/Camera/IMG_20160807_133403.jpg路徑,也是使用這種方法
 4      * @param context
 5      * @param uri
 6      * @return
 7      */
 8     public static String getFilePath_below19(Context context,Uri uri) {
 9         //這裡開始的第二部分,獲取圖片的路徑:低版本的是沒問題的,但是sdk>19會獲取不到
10         Cursor cursor = null;
11         String path = "";
12         try {
13             String[] proj = {MediaStore.Images.Media.DATA};
14             //好像是android多媒體資料庫的封裝介面,具體的看Android文檔
15             cursor = context.getContentResolver().query(uri, proj, null, null, null);
16             //獲得用戶選擇的圖片的索引值
17             int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
18             //將游標移至開頭 ,這個很重要,不小心很容易引起越界
19             cursor.moveToFirst();
20             //最後根據索引值獲取圖片路徑   結果類似:/mnt/sdcard/DCIM/Camera/IMG_20151124_013332.jpg
21             path = cursor.getString(column_index);
22         } finally {
23             if (cursor != null) {
24                 cursor.close();
25             }
26         }
27         return path;
28     }

2.2.2 對於Android 4.4及以上機型獲取path

  1 /**
  2      * @param context 上下文對象
  3      * @param uri     當前相冊照片的Uri
  4      * @return 解析後的Uri對應的String
  5      */
  6     @SuppressLint("NewApi")
  7     public static String getPath(final Context context, final Uri uri) {
  8 
  9         final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
 10         String pathHead = "file:///";
 11         // 1. DocumentProvider
 12         if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
 13             // 1.1 ExternalStorageProvider
 14             if (isExternalStorageDocument(uri)) {
 15                 final String docId = DocumentsContract.getDocumentId(uri);
 16                 final String[] split = docId.split(":");
 17                 final String type = split[0];
 18                 if ("primary".equalsIgnoreCase(type)) {
 19                     return pathHead + Environment.getExternalStorageDirectory() + "/" + split[1];
 20                 }
 21             }
 22             // 1.2 DownloadsProvider
 23             else if (isDownloadsDocument(uri)) {
 24                 final String id = DocumentsContract.getDocumentId(uri);
 25                 final Uri contentUri = ContentUris.
 26                         withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
 27                 return pathHead + getDataColumn(context,
 28                         contentUri, null, null);
 29             }
 30             // 1.3 MediaProvider
 31             else if (isMediaDocument(uri)) {
 32                 final String docId = DocumentsContract.getDocumentId(uri);
 33                 final String[] split = docId.split(":");
 34                 final String type = split[0];
 35 
 36                 Uri contentUri = null;
 37                 if ("image".equals(type)) {
 38                     contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
 39                 } else if ("video".equals(type)) {
 40                     contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
 41                 } else if ("audio".equals(type)) {
 42                     contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
 43                 }
 44 
 45                 final String selection = "_id=?";
 46                 final String[] selectionArgs = new String[]{split[1]};
 47 
 48                 return pathHead + getDataColumn(context, contentUri, selection, selectionArgs);
 49             }
 50         }
 51         // 2. MediaStore (and general)
 52         else if ("content".equalsIgnoreCase(uri.getScheme())) {
 53             if (isGooglePhotosUri(uri)) {//判斷是否是google相冊圖片
 54                 return uri.getLastPathSegment();
 55             } else if (isGooglePlayPhotosUri(uri)) {//判斷是否是Google相冊圖片
 56                 return getImageUrlWithAuthority(context, uri);
 57             } else {//其他類似於media這樣的圖片,和android4.4以下獲取圖片path方法類似
 58                 return getFilePath_below19(context, uri);
 59             }
 60         }
 61         // 3. 判斷是否是文件形式 File
 62         else if ("file".equalsIgnoreCase(uri.getScheme())) {
 63             return pathHead + uri.getPath();
 64         }
 65         return null;
 66     } 
 67 /**
 68      * @param uri
 69      *         The Uri to check.
 70      * @return Whether the Uri authority is ExternalStorageProvider.
 71      */
 72     private static boolean isExternalStorageDocument(Uri uri) {
 73         return "com.android.externalstorage.documents".equals(uri.getAuthority());
 74     }
 75 
 76     /**
 77      * @param uri
 78      *         The Uri to check.
 79      * @return Whether the Uri authority is DownloadsProvider.
 80      */
 81     private static boolean isDownloadsDocument(Uri uri) {
 82         return "com.android.providers.downloads.documents".equals(uri.getAuthority());
 83     }
 84 
 85     /**
 86      * @param uri
 87      *         The Uri to check.
 88      * @return Whether the Uri authority is MediaProvider.
 89      */
 90     private static boolean isMediaDocument(Uri uri) {
 91         return "com.android.providers.media.documents".equals(uri.getAuthority());
 92     }
 93 /**
 94  *  判斷是否是Google相冊的圖片,類似於content://com.google.android.apps.photos.content/...
 95 **/
 96 public static boolean isGooglePhotosUri(Uri uri) {
 97          return "com.google.android.apps.photos.content".equals(uri.getAuthority());
 98     }
 99 
100 /**
101  *  判斷是否是Google相冊的圖片,類似於content://com.google.android.apps.photos.contentprovider/0/1/mediakey:/local%3A821abd2f-9f8c-4931-bbe9-a975d1f5fabc/ORIGINAL/NONE/1075342619
102 **/
103 public static boolean isGooglePlayPhotosUri(Uri uri) {
104     return "com.google.android.apps.photos.contentprovider".equals(uri.getAuthority());
105 } 

2.2.3 針對Google相冊圖片獲取方法

從相冊中選擇圖片,如果手機安裝了Google Photo,它的路徑格式如下: 
content://com.google.android.apps.photos.contentprovider/0/1/mediakey%3A%2Flocal%253A821abd2f-9f8c-4931-bbe9-a975d1f5fabc/ORIGINAL/NONE/1754758324 
用原來的方式獲取是不起作用的,path會是null,我們可以通過下麵的形式獲取:

 1 /**
 2  * Google相冊圖片獲取路徑
 3 **/
 4 public static String getImageUrlWithAuthority(Context context, Uri uri) {
 5         InputStream is = null;
 6         if (uri.getAuthority() != null) {
 7             try {
 8                 is = context.getContentResolver().openInputStream(uri);
 9                 Bitmap bmp = BitmapFactory.decodeStream(is);
10                 return writeToTempImageAndGetPathUri(context, bmp).toString();
11             } catch (FileNotFoundException e) {
12                 e.printStackTrace();
13             }finally {
14                 try {
15                     is.close();
16                 } catch (IOException e) {
17                     e.printStackTrace();
18                 }
19             }
20         }
21         return null;
22     }
23 /**
24  * 將圖片流讀取出來保存到手機本地相冊中
25 **/
26 public static Uri writeToTempImageAndGetPathUri(Context inContext, Bitmap inImage) {
27     ByteArrayOutputStream bytes = new ByteArrayOutputStream();
28     inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
29     String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
30     return Uri.parse(path);
31 }   

通過流的形式將圖片讀進來,轉成bitmap形式,再寫進手機媒體中,轉換成路徑如下:content://media/external/images/media/1754758324,因為Google中的圖片並不在我們系統手機相冊中,需要先下載,再轉存。

3. 針對Android 7.0及以上機型打開Uri適配

Android7.0以上機型,若要在應用間共用文件,應發送一項 content:// URI,並授予 Uri臨時訪問許可權。進行此授權的最簡單方式是使用 FileProvider 類。如需瞭解有關許可權和共用文件的詳細信息,請參閱官方的共用文件

具體需要在AndroidMainfest.xml文件中添加provider標簽,並定義相應的路徑,具體可參考鴻洋寫的Android 7.0 行為變更 通過FileProvider在應用間共用文件吧 
具體裁剪圖片代碼如下

 1 /**
 2      * @param activity  當前activity
 3      * @param orgUri 剪裁原圖的Uri
 4      * @param desUri 剪裁後的圖片的Uri
 5      * @param aspectX X方向的比例
 6      * @param aspectY  Y方向的比例
 7      * @param width 剪裁圖片的寬度
 8      * @param height 剪裁圖片高度
 9      * @param requestCode  剪裁圖片的請求碼
10      */
11     public static void cropImageUri(Activity activity, Uri orgUri,
12                                     Uri desUri, int aspectX, int aspectY,
13                                     int width, int height, int requestCode) {
14         Intent intent = new Intent("com.android.camera.action.CROP");
15         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
16             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
17             orgUri = FileProvider.getUriForFile(activity, "com.smilexie.storytree.fileprovider", new File(newUri.getPath()));
18         }
19         intent.setDataAndType(orgUri, "image/*");
20         intent.putExtra("crop", "true");
21         intent.putExtra("outputX", width);
22         intent.putExtra("outputY", height);
23         intent.putExtra("scale", true);
24         //將剪切的圖片保存到目標Uri中
25         intent.putExtra(MediaStore.EXTRA_OUTPUT, desUri);
26         intent.putExtra("return-data", false);
27         intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
28         intent.putExtra("noFaceDetection", true);
29         activity.startActivityForResult(intent, requestCode);
30     }

只要是打開文件,uri都需要更改,不然解析異常,但是保存進uri則不需要修改。

總結

Android各版本,各機型適配不得不說有很多坑,比如我在開發中遇到google相冊圖片,老是獲取不到路徑。只能平時多關註Android新版本發佈時,會對現有應用產生什麼影響,遇到問題多查看別人的解決辦法,多歸納,多總結,這樣才能使咱們的應用越來越完善。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # 配置相關 0. 配置存儲 在$HOME目錄下的.hiverc文件中 1. 在提示符中顯示資料庫名 set hive.cli.print.current.db=true; 2. 優先使用本地模式執行 set hive.exec.mode.loacl.auto=true; 3. 列印列名 set h ...
  • 在給一個表添加欄位的時候,忽然發現會報一個date類型的欄位的預設值錯誤,鬱悶~ 經過排查,原來是MySQL的配置問題,在wamp下,MySQL 5.7里是沒有設置 SQL_MODE 的。 1.my.ini文件中找到 [mysqld] 2.如果沒有SQL_MODE,就添加,有就修改一下 3.重啟My ...
  • 註意: 有的Centos版本預設安裝了mariadb, 可以先將其卸載 檢查mariadb是否安裝 yum list installed | grep mariadb 卸載mariadb( all ) yum -y remove mariadb* 1. 網上下載MySQL的yum源 wget htt ...
  • 今日內容介紹 1.MySQL資料庫 2.SQL語句 01資料庫概念 A: 什麼是資料庫 資料庫就是存儲數據的倉庫,其本質是一個文件系統,數據按照特定的格式將數據存儲起來, 用戶可以對資料庫中的數據進行增加,修改,刪除及查詢操作。 B: 什麼是資料庫管理系統 資料庫管理系統(DataBase Mana ...
  • 一、簡介 pt-query-digest是用於分析mysql慢查詢的一個工具,它可以分析binlog、General log、slowlog,也可以通過SHOWPROCESSLIST或者通過tcpdump抓取的MySQL協議數據來進行分析。可以把分析結果輸出到文件中,分析過程是先對查詢語句的條件進行 ...
  • 1、平臺: Windows10 Pycharm 2018.1 Python 3.6.4 2、問題: 我在Pycharm下建立一個flask_ab工程,導入包: 運行時報錯:“ImportError: No module named flask_sqlalchemy” 3、解決方案: 打開File-> ...
  • 字元串作為OC語言的基本對象類型,被我們在各種地方廣泛使用,因此熟悉掌握字元串的屬性和方法,是OC必不可少的基礎之一。 字元串分為可變字元串(NSMutableString)和不可變字元串(NSString),首先我們來學習不可變字元串的屬性與方法。 一、不可變字元串 1.創建不可變字元串 主要有四 ...
  • 觀察者模式的概念 觀察者A與被觀察者B建立訂閱關係,當被觀察者B發生某種改變時,立即通知觀察者A 添加依賴 基本模式 Observable被觀察者 註意各地方添加泛型避免大片警告,onNext()是事件的回調,onComplete()是事件的結尾。onComplete()與onError互斥需要保持 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...