### FileProvider 先前有一篇文章提及到為什麼會有 FileProvider 的出現,內容可以參考那篇文章。 [Android Intent 透過 FileProvider 分享檔案的使用權限](https://cdn.19site.net/posts/113) 主要也是為了加強對檔案的安全性管理及加強對內容檔案的操作強化。 ### 如何設定 FileProvider 我們要先在檔案 `AndroidManifest.xml` 一段對 Provider 的定義 : ```xml <provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:enabled="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> ``` 然後在 `res/xml/file_paths.xml` 加入以下內容,沒有檔案不存在就建立一個新檔案 : ```xml <?xml version="1.0" encoding="utf-8"?> <paths> <external-files-path name="external_files" path="." /> </paths> ``` 依照官方的說明文件,這這檔案可以設定更多的內容,使這個 APK 能支援更多的檔案輸出路徑。 https://developer.android.com/reference/androidx/core/content/FileProvider 以下是 XML 設定對應的 Java 方法表格 : |XML 設定|Java 方法| |---| |files-path|Context.getFilesDir()| |cache-path|Context.getCacheDir()| |external-path|Environment.getExternalStorageDirectory()| |external-files-path|Context#getExternalFilesDir(String), Context.getExternalFilesDir(null)| |external-cache-path|Context.getExternalCacheDir()| |external-media-path|Context.getExternalMediaDirs()| 只要設定好對的內容,就可以讓 Intent 的目標讀取得到 URI 的內容。 ### 實例 以下是把一個儲存在 APP 自己 DATA 內的檔案想透過 FileProvider 讓外部可以讀取。 檔案 `AndroidManifest.xml` 有以下設定 : ```xml <provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:enabled="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> ``` 檔案 `res/xml/file_paths.xml` 有以下設定 : ```xml <?xml version="1.0" encoding="utf-8"?> <paths> <files-path name="my_videos" path="videos/" /> <files-path name="my_images" path="images/" /> </paths> ``` 然後在 Java 程式內使用 FileProvider 取得檔案的 Uri : ```java // video file path File mFilePath = new File(context.getFilesDir(), "videos"); // file from application directory File mFile = new File(mFilePath, "myvideo.mp4"); // get uri via file provider Uri mUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", mFile); mUri.toString(); >>> content://com.example.fileprovider/my_videos/myvideo.mp4 ``` 我們會發現 FileProvider 還可以改寫來自不同 Directory 的名稱,甚至可以用來限制只輸出某一個 sub directory 的檔案,達到進一步的安全效果。 再看看下一個例子 : ```java // video file path File mFilePath = new File(context.getFilesDir(), "images"); // file from application directory File mFile = new File(mFilePath, "myimage.jpg"); // get uri via file provider Uri mUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", mFile); mUri.toString(); >>> content://com.example.fileprovider/my_images/myimage.jpg ``` 從輸出的效果我們可以推算出 `res/xml/file_paths.xml` 設定對 FileProvider 輸出的影響。 ### 秘技 功能愈多設定也會愈複雜,要一個個路徑設定到 `file_paths.xml` 實在是費時又易出錯,所以在普通的情況下,大家可以使用以下的設定來減輕功夫。 ```xml <?xml version="1.0" encoding="utf-8"?> <paths> <external-path name="external" path="." /> <external-files-path name="external_files" path="." /> <cache-path name="cache" path="." /> <external-cache-path name="external_cache" path="." /> <files-path name="files" path="." /> </paths> ``` 要注意,以上的設定可以把你 App 內的所有檔案透過 FileProvider 分享出去。不過也不必太擔心,因為只能透過 FileProvider 主動分享出去,對算對方 App 知道 content uri 也好,也是無法讀取到檔案的。