在設計Android App時,常常需要讓使用者選擇自己裝置內的圖片、音樂、影片或是文件,來進行一些處理,例如上傳檔案或是編輯檔案等等的功能。但是要如何讓使用者選擇檔案呢?Java程式語言的Swing函式庫中,提供了JFileChooser類別,可以直接產生出一個檔案選取的視窗,能讓使用者選取電腦內的檔案,還可以使用FileFilter過濾檔案選取視窗顯示出來的檔案類型,十分方便。雖然Android的程式是使用Java程式語言進行開發,但Android上的Java只能算是半殘的Java,有許多類別不能使用,其中當然包括JFileChooser,不然就不用著寫這篇文章了。
要讓Android程式擁有檔案選取的功能,最保險、可靠而且一定能Work的方式,那就是自己使用File類別或是ContentProvider實作出一個能選取檔案的Activity或是Dialog。如果不知道要怎麼自己做出檔案選取器的話,也可以使用別人寫好的,在網路上都可以找得到程式碼,只是品質無法保證。
這篇文章將不探討如何直接在自己的Android App內實作出檔案選取器,而是要應用Android裝置已經安裝的檔案瀏覽器之類的App來選取檔案。舉例來說,假設要選取一張圖片,而且裝置上已經有安裝「圖片庫(Google Gallery)」或是「快圖瀏覽(QuickPic)」之類的App,那麼將可以直接使用它們來選取圖片,就不需要在程式專案內實作出複雜的檔案選取和圖片瀏覽的功能了!
Android上的檔案類型
在選取檔案之前,必須要清楚知道要選取什麼樣類型的檔案。在Android系統中,跟內容(Content)相關的資訊都可由ContentProvider來取得,當使用者儲存了一個檔案,檔案的實際路徑與類型就會被儲存在ContentProvider中,因此只要查找ContentProvider中的檔案類型,就可以很容易地取得裝置中所有屬於這種類型的檔案。
Android的檔案類型採用MIME(Multipurpose Internet Mail Extensions)標準,MIME使用特定的字串格式來表示內容類型(當然也包括檔案),MIME的字串格式如下:
以檔案來說,可能會有以下幾種類型:
- text
- text/plain:純文字文件。
- text/html:HTML文件。
- application
- application/xhtml+xml:XHTML文件。
- application/pdf:PDF文件。
- image
- image/gif:GIF圖片。
- image/jpeg:JPEG圖片。
- image/png:PNG圖片。
- audio
- audio/midi:MIDI音樂。
- audio/x-mpeg:MP2或是MP3音樂。
- audio/x-wav:WAV音樂。
- video
- video/mp4:MP4影片。
- video/mpeg:MPEG影片。
- video/3gpp:3GP影片。
如果要表示所有類型的檔案,可以使用「*」字元。舉例來說,MIME類型若為「image/*」,表示所有的圖片類型;MIME類型若為「*/*」表示所有類型。
透過MIME選取檔案
有了其他Android App的幫助,設計師就不需要自己使用ContentProvider來取的指定MIME類型下的檔案清單,只需要讓這些擁有檔案選取功能的App知道應該要選擇什麼樣MIME類型的檔案即可。
Android的Intent可以用在Application或是Activity之間的溝通,只要使用Intent的ACTION_GET_CONTENT,系統就會幫使用者找到裝置內合適的App來取得指定MIME類型的內容,程式可以這樣寫:
...
Intent picker = new Intent(Intent.ACTION_GET_CONTENT);
picker.setType("*/*");
startActivity(picker);
建議用法
如果程式只有寫成這樣的話,將會遇到兩個恰恰相反的問題。第一個是Android系統找不到符合指定MIME類型的內容選取器,程式將會因為找不到可執行的Activity而直接閃退,這個問題甚至可能會沒辦法直接用try-catch來解決。第二個可能會遇到的問題是,當Android系統找到兩種以上可用的App或是Activity支援指定的MIME類型時,可能會自動使用其中一種,此時也許就會選到無法正常使用的App或是Activity,連帶使我們的App永遠無法正常使用。
要解決第一個找不到Activity的問題,可以事先使用PackageManager查詢可以使用該MIME類型的Activity列表來解決。而要解決第二個可用App或是Activity有兩個以上的問題的話,可以使用系統內建的Intent Chooser,跳出選單讓使用者選擇要使用哪個。因此選取檔案的程式可以寫成如以下的樣子,大概就沒有什麼問題了。
final String mimeType = "image/png";
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType(mimeType);
List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (list.size() > 0) {
// 如果有可用的Activity
Intent picker = new Intent(Intent.ACTION_GET_CONTENT);
picker.setType(mimeType);
// 使用Intent Chooser
Intent destIntent = Intent.createChooser(picker, "選取PNG圖片");
startActivity(destIntent);
} else {
// 沒有可用的Activity
...
}
複選檔案
如果要複選檔案,可以在picker的Intent中,加入EXTRA_ALLOW_MULTIPLE欄位,並將其值設為true,如下:
picker.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
EXTRA_ALLOW_MULTIPLE不一定能在所有的Activity上成功使用,要看該Activity有沒有用到EXTRA_ALLOW_MULTIPLE,並且實作出檔案複選的功能。
只能選擇本地端的檔案
如果只要選擇裝置內本地端的檔案,也可以在picker的Intent中,加入EXTRA_LOCAL_ONLY欄位,並將其值設為true。
picker.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
至於什麼時候有能選擇裝置外的檔案的情況,當Activity有瀏覽雲端檔案或是同步之類的功能時,所選的檔案就可能不在本地端,如果此時要抓取檔案的實際路徑來做些應用,就可能會因實際路徑不在本地端上,而出現問題。EXTRA_LOCAL_ONLY也是一樣不一定能在所有的Activity上成功使用,要看該Activity的實作方式。
常見的錯誤用法
網路上常常可以看到有別人是使用Intent的ACTION_PICK來選取檔案,寫法其實和ACTION_GET_CONTENT類似,如下:
...
Intent picker = new Intent(Intent.ACTION_PICK);
picker.setType("*/*");
startActivity(picker);
雖然執行結果好像跟ACTION_GET_CONTENT沒有兩樣,但是其實這樣的用法是錯誤的。原因在於ACTION_PICK實際上是用來選擇一個URI目錄下的資料,比較正確的用法如下:
final Uri uri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(Intent.ACTION_PICK, uri);
List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (list.size() > 0) {
// 如果有可用的Activity
Intent picker = new Intent(Intent.ACTION_PICK, uri);
// 使用Intent Chooser
Intent destIntent = Intent.createChooser(picker, "選取圖片");
startActivity(destIntent);
} else {
// 沒有可用的Activity
...
}
取得檔案位於裝置上的實際路徑
使用Intent的ACTION_GET_CONTENT和MIME類型來選擇檔案十分容易,但要如何取得使用者選定的檔案呢?那就得看使用者所用來選取檔案的Activity,是如何傳回檔案資料的,這部份並沒有特定的格式,而且也可能會有不符合指定MIME類型的檔案。
但是,大部份可選擇檔案的Activity都是在onActivityResult時,將選擇的結果包在傳回的Intent內。如果要使用onActivityResult來取得Activity的回傳結果,那麼當初在開啟Activity時,就要使用startActivityForResult方法,而不是startActivity方法。選取的結果,單選和複選的狀況應該要分開探討。
單選檔案
使用getData方法取得傳回的Intent所包含的URI,這個URI通常可以直接被用來反查ContentProvider內的檔案實際路徑。
Uri uri = intent.getData();
複選檔案
Intent的EXTRA_ALLOW_MULTIPLE欄位定義是在Android API 18之後才出現的,但是API 18之前的Activity究竟有沒有支援檔案複選也很難說。可以確定的是,複選檔案的結果大多是包在Intent的ClipData中,因此可以使用getClipData方法取得傳回的Intent所包含的ClipData,再用迴圈抓出ClipData中所有項目的URI,這個URI通常可以直接被用來反查ContentProvider內的檔案實際路徑。建議從Android API 16開始支援檔案複選的功能。
if(Build.VERSION.SDK_INT >= 16){
ClipData clipData = intent.getClipData();
int count = clipData.getItemCount();
if (count > 0) {
Uri[] uris = new Uri[count];
for (int i = 0; i < count; i++) {
uris[i] = clipData.getItemAt(i).getUri();
}
}
}
實際路徑
有了檔案的URI後,接著就要找出URI所對應的檔案,在Android裝置上實際的路徑。在Android 4.4(API 19)之前的版本,URI都可以直接透過ContentProvider來查詢其對應的檔案實際路徑,但在Android 4.4以及之後的版本,系統似乎內建了內容瀏覽器,並且使用了Android 4.4之後才新增的文件URI格式,回傳的URI Authority將會使用「com.android.externalstorage.documents」、「com.android.providers.downloads.documents」或是「com.android.providers.media.documents」。如果直接用這樣的URI查詢ContentProvider,將會得到空的路徑,因此這樣的URI需要進行一些轉換,才能被用來查詢ContentProvider。
實作檔案選取的程式
由於檔案選取算是一個常用的功能,所以可以將它寫成一個獨立的類別來重複使用。程式碼如下:
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ClipData;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
/**
* 檔案選取器,支援檔案MIME類別篩選、強制可讀取以及複選功能。
*
* @author Magic Len
*/
@SuppressLint("NewApi")
public class MagicFileChooser {
// TODO ------類別常數------
/**
* 檔案選取器的Activity Request Code。
*/
public static final int ACTIVITY_FILE_CHOOSER = 9973;
// TODO -----物件變數-----
/**
* 儲存使用這個檔案選取器的Activity。
*/
private final Activity activity;
// TODO -----物件變數-----
/**
* 儲存是否正在選取檔案。
*/
private boolean choosing = false;
/**
* 儲存被選到的檔案是否一定要可以讀取。
*/
private boolean mustCanRead;
/**
* 儲存被選到的檔案。
*/
private File[] chosenFiles;
// TODO -----建構子-----
/**
* 建構子,在Activity內使用檔案選取器。
*
* @param activity 傳入使用這個檔案選取器的Activity。
*/
public MagicFileChooser(final Activity activity) {
this.activity = activity;
}
// TODO -----類別方法-----
/**
* 從Uri取得絕對路徑。
*
* @param context 傳入Context
* @param uris 傳入Uri陣列
* @return 傳回絕對路徑字串陣列,若絕對路徑無法取得,則對應的陣列索引位置為null
*/
public static String[] getAbsolutePathsFromUris(final Context context, final Uri[] uris) {
return getAbsolutePathsFromUris(context, uris, false);
}
/**
* 從多個Uri取得絕對路徑。
*
* @param context 傳入Context
* @param uris 傳入Uri陣列
* @param mustCanRead 傳入Uri所指的路徑是否一定要可以讀取
* @return 傳回絕對路徑字串陣列,若絕對路徑無法取得或是無法讀取,則對應的陣列索引位置為null
*/
public static String[] getAbsolutePathsFromUris(final Context context, final Uri[] uris, final boolean mustCanRead) {
if (uris == null) {
return null;
}
final int urisLength = uris.length;
final String[] paths = new String[urisLength];
for (int i = 0; i < urisLength; ++i) {
final Uri uri = uris[i];
paths[i] = getAbsolutePathFromUri(context, uri, mustCanRead);
}
return paths;
}
/**
* 從多個Uri取得File物件。
*
* @param context 傳入Context
* @param uris 傳入Uri陣列
* @return 傳回File物件陣列,若File物件無法建立,則對應的陣列索引位置為null
*/
public static File[] getFilesFromUris(final Context context, final Uri[] uris) {
return getFilesFromUris(context, uris, false);
}
/**
* 從多個Uri取得File物件。
*
* @param context 傳入Context
* @param uris 傳入Uri陣列
* @param mustCanRead 傳入Uri所指的路徑是否一定要可以讀取
* @return 傳回File物件陣列,若File物件無法建立或是檔案路徑無法讀取,則對應的陣列索引位置為null
*/
public static File[] getFilesFromUris(final Context context, final Uri[] uris, final boolean mustCanRead) {
if (uris == null) {
return null;
}
final int urisLength = uris.length;
final File[] files = new File[urisLength];
for (int i = 0; i < urisLength; ++i) {
final Uri uri = uris[i];
files[i] = getFileFromUri(context, uri, mustCanRead);
}
return files;
}
/**
* 從Uri取得絕對路徑。
*
* @param context 傳入Context
* @param uri 傳入Uri物件
* @return 傳回絕對路徑,若絕對路徑無法取得,傳回null
*/
public static String getAbsolutePathFromUri(final Context context, final Uri uri) {
return getAbsolutePathFromUri(context, uri, false);
}
/**
* 從Uri取得絕對路徑。
*
* @param context 傳入Context
* @param uri 傳入Uri物件
* @param mustCanRead 傳入Uri所指的路徑是否一定要可以讀取
* @return 傳回絕對路徑,若絕對路徑無法取得或是無法讀取,傳回null
*/
public static String getAbsolutePathFromUri(final Context context, final Uri uri, final boolean mustCanRead) {
final File file = getFileFromUri(context, uri, mustCanRead);
if (file != null) {
return file.getAbsolutePath();
} else {
return null;
}
}
/**
* 從Uri取得File物件。
*
* @param context 傳入Context
* @param uri 傳入Uri物件
* @return 傳回File物件,若File物件無法建立,傳回null
*/
public static File getFileFromUri(final Context context, final Uri uri) {
return getFileFromUri(context, uri, false);
}
/**
* 從Uri取得File物件。
*
* @param context 傳入Context
* @param uri 傳入Uri物件
* @param mustCanRead 傳入Uri所指的路徑是否一定要可以讀取
* @return 傳回File物件,若File物件無法建立或是檔案路徑無法讀取,傳回null
*/
@SuppressLint("NewApi")
public static File getFileFromUri(final Context context, final Uri uri, final boolean mustCanRead) {
if (uri == null) {
return null;
}
// 判斷是否為Android 4.4之後的版本
final boolean after44 = Build.VERSION.SDK_INT >= 19;
if (after44 && DocumentsContract.isDocumentUri(context, uri)) {
// 如果是Android 4.4之後的版本,而且屬於文件URI
final String authority = uri.getAuthority();
// 判斷Authority是否為本地端檔案所使用的
if ("com.android.externalstorage.documents".equals(authority)) {
// 外部儲存空間
final String docId = DocumentsContract.getDocumentId(uri);
final String[] divide = docId.split(":");
final String type = divide[0];
if ("primary".equals(type)) {
String path = Environment.getExternalStorageDirectory().getAbsolutePath().concat("/").concat(divide[1]);
return createFileObjFromPath(path, mustCanRead);
} else {
String path = "/storage/".concat(type).concat("/").concat(divide[1]);
return createFileObjFromPath(path, mustCanRead);
}
} else if ("com.android.providers.downloads.documents".equals(authority)) {
// 下載目錄
final String docId = DocumentsContract.getDocumentId(uri);
if (docId.startsWith("raw:")) {
final String path = docId.replaceFirst("raw:", "");
return createFileObjFromPath(path, mustCanRead);
}
final Uri downloadUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.parseLong(docId));
String path = queryAbsolutePath(context, downloadUri);
return createFileObjFromPath(path, mustCanRead);
} else if ("com.android.providers.media.documents".equals(authority)) {
// 圖片、影音檔案
final String docId = DocumentsContract.getDocumentId(uri);
final String[] divide = docId.split(":");
final String type = divide[0];
Uri mediaUri = null;
if ("image".equals(type)) {
mediaUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
mediaUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
mediaUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
} else {
return null;
}
mediaUri = ContentUris.withAppendedId(mediaUri, Long.parseLong(divide[1]));
String path = queryAbsolutePath(context, mediaUri);
return createFileObjFromPath(path, mustCanRead);
}
} else {
// 如果是一般的URI
final String scheme = uri.getScheme();
String path = null;
if ("content".equals(scheme)) {
// 內容URI
path = queryAbsolutePath(context, uri);
} else if ("file".equals(scheme)) {
// 檔案URI
path = uri.getPath();
}
return createFileObjFromPath(path, mustCanRead);
}
return null;
}
/**
* 將路徑轉成File物件。
*
* @param path 傳入檔案路徑
* @return 傳回File物件,若File物件無法建立,傳回null。
*/
public static File createFileObjFromPath(final String path) {
return createFileObjFromPath(path, false);
}
/**
* 將路徑轉成File物件。
*
* @param path 傳入檔案路徑
* @param mustCanRead 傳入檔案路徑是否一定要可以讀取
* @return 傳回File物件,若File物件無法建立或是檔案路徑無法讀取,傳回null
*/
public static File createFileObjFromPath(final String path, final boolean mustCanRead) {
if (path != null) {
try {
File file = new File(path);
if (mustCanRead) {
file.setReadable(true);
if (!file.canRead()) {
return null;
}
}
return file.getAbsoluteFile();
} catch (Exception ex) {
ex.printStackTrace();
}
}
return null;
}
/**
* 查詢MediaStroe Uri對應的絕對路徑。
*
* @param context 傳入Context
* @param uri 傳入MediaStore Uri
* @return 傳回絕對路徑
*/
public static String queryAbsolutePath(final Context context, final Uri uri) {
final String[] projection = {MediaStore.MediaColumns.DATA};
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri, projection, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
return cursor.getString(index);
}
} catch (final Exception ex) {
ex.printStackTrace();
if (cursor != null) {
cursor.close();
}
}
return null;
}
// TODO -----物件方法-----
/**
* 顯示檔案選取器,選取所有檔案,不設定檔案選取器的標題,僅進行單獨選取,被選到的檔案不一定要可以讀取。
*
* @return 傳回檔案選取器是否開啟成功
*/
public boolean showFileChooser() {
return showFileChooser("*/*");
}
/**
* 顯示檔案選取器,不設定檔案選取器的標題,僅進行單獨選取,被選到的檔案不一定要可以讀取。
*
* @param mimeType 傳入篩選的MIME類型
* @return 傳回檔案選取器是否開啟成功
*/
public boolean showFileChooser(final String mimeType) {
return showFileChooser(mimeType, null);
}
/**
* 顯示檔案選取器,僅進行單獨選取,被選到的檔案不一定要可以讀取。
*
* @param mimeType 傳入篩選的MIME類型
* @param chooserTitle 傳入檔案選取器的標題,若為null則用預設值
* @return 傳回檔案選取器是否開啟成功
*/
public boolean showFileChooser(final String mimeType, final String chooserTitle) {
return showFileChooser(mimeType, chooserTitle, false);
}
/**
* 顯示檔案選取器,被選到的檔案不一定要可以讀取。
*
* @param mimeType 傳入篩選的MIME類型
* @param chooserTitle 傳入檔案選取器的標題,若為null則用預設值
* @param allowMultiple 傳入檔案選取器是否使用複選
* @return 傳回檔案選取器是否開啟成功
*/
public boolean showFileChooser(final String mimeType, final String chooserTitle, final boolean allowMultiple) {
return showFileChooser(mimeType, chooserTitle, allowMultiple, false);
}
/**
* 顯示檔案選取器。
*
* @param mimeType 傳入篩選的MIME類型
* @param chooserTitle 傳入檔案選取器的標題,若為null則用預設值
* @param allowMultiple 傳入檔案選取器是否使用複選
* @param mustCanRead 傳入被選到的檔案是否一定要可以讀取
* @return 傳回檔案選取器是否開啟成功
*/
public boolean showFileChooser(final String mimeType, final String chooserTitle, final boolean allowMultiple, final boolean mustCanRead) {
if (mimeType == null || choosing) {
return false;
}
choosing = true;
// 檢查是否有可用的Activity
final PackageManager packageManager = activity.getPackageManager();
final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType(mimeType);
final List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (list.size() > 0) {
this.mustCanRead = mustCanRead;
// 如果有可用的Activity
final Intent picker = new Intent(Intent.ACTION_GET_CONTENT);
picker.setType(mimeType);
picker.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultiple);
picker.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
// 使用Intent Chooser
final Intent destIntent = Intent.createChooser(picker, chooserTitle);
activity.startActivityForResult(destIntent, ACTIVITY_FILE_CHOOSER);
return true;
} else {
return false;
}
}
/**
* 當檔案選取器被關閉後,應該要呼叫這個方法,判斷檔案選取器是否有選取到檔案,接著再用getChosenFiles方法來取得選取結果。
*
* @param requestCode 傳入Activity的Request Code
* @param resultCode 傳入Activity的Request Code
* @param data 傳入Activity的data
* @return 傳回檔案選取器是否有選取結果。
*/
public boolean onActivityResult(final int requestCode, final int resultCode, final Intent data) {
if (requestCode == ACTIVITY_FILE_CHOOSER) {
choosing = false;
if (resultCode == Activity.RESULT_OK) {
final Uri uri = data.getData();
if (uri != null) {
// 單選
chosenFiles = getFilesFromUris(activity, new Uri[]{uri}, mustCanRead);
return true;
} else if (Build.VERSION.SDK_INT >= 16) {
// 複選
final ClipData clipData = data.getClipData();
if (clipData != null) {
int count = clipData.getItemCount();
if (count > 0) {
final Uri[] uris = new Uri[count];
for (int i = 0; i < count; ++i) {
uris[i] = clipData.getItemAt(i).getUri();
}
chosenFiles = getFilesFromUris(activity, uris, mustCanRead);
return true;
}
}
}
}
}
return false;
}
/**
* 取得被選取到的檔案。
*
* @return 傳回被選取到的檔案,過濾掉不成功的部份
*/
public File[] getChosenFiles(final boolean filter) {
if (chosenFiles == null) {
return new File[0];
} else {
final ArrayList<File> alFileList = new ArrayList<>();
for (final File chosenFile : chosenFiles) {
if (filter && chosenFile == null) {
continue;
}
alFileList.add(chosenFile);
}
final File[] files = new File[alFileList.size()];
alFileList.toArray(files);
return files;
}
}
/**
* 取得被選取到的檔案。
*
* @return 傳回被選取到的檔案,過濾掉不成功的部份
*/
public File[] getChosenFiles() {
return getChosenFiles(true);
}
}
MagicFileChooser的用法十分容易,如果只是要用來取得URI的實體路徑,甚至不用使用new運算子來實體化出MagicFileChooser物件。
如果要使用MagicFileChooser來選取檔案的話,一開始必須先將MagicFileChooser實體化,並且要在建構子的參數傳入當前的Activity。接著使用showFileChooser方法來顯示出Android裝置內可用的檔案選取App,或是檔案選取App的列表。如果showFileChooser方法回傳false,表示裝置內並沒有適合的Activity能作為檔案選取器。為了取得MagicFileChooser的檔案選取結果,必須要覆寫Activity的onActivityResult方法,要在Activity的onActivityResult方法中呼叫MagicFileChooser的onActivityResult方法。如果選取結果有回傳成功,MagicFileChooser的onActivityResult方法會傳回true,此時可以使用MagicFileChooser的getChosenFiles方法,取得使用者所選的檔案,型態為File陣列。如此一來變能輕鬆無負擔地選取檔案了!