android webview 文件上傳

1、喚出系統(tǒng)文件管理器

開啟文件上傳,可使用HTML5標(biāo)簽 <input type="file"> 喚出系統(tǒng)文件管理器或自定義文件管理器,然后選擇文件。

MainActivity.java:

private WebView webView;
private WVChromeClient wv = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
    setContentView(R.layout.activity_main);
    webView = (WebView) findViewById(R.id.wv_webview);
    WebSettings settings = webView.getSettings();
    settings.setUseWideViewPort(true);
    settings.setJavaScriptEnabled(true);
    wv = new WVChromeClient(this,MainActivity.this);
    webView.setWebChromeClient(wv);
}

 @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == WVChromeClient.CHOOSER_REQUEST) { // 處理返回的文件
        wv.onActivityResultFileChooser(requestCode, resultCode, data); // 調(diào)用 WVChromeClient 類中的 回調(diào)方法
    }
}

WVChromeClient.java:

public class WVChromeClient extends WebChromeClient {
    private static final String TAG = "WebChromeClient:";
    public final static int CHOOSER_REQUEST = 0x33;
    private ValueCallback<Uri[]> uploadFiles = null;
    Context context;
    MainActivity _m;
    public WVChromeClient(Context _context, MainActivity mainActivity)
    {
        context = _context;
        _m = mainActivity;
    }

    // 第一種方式
    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
                                     FileChooserParams fileChooserParams) {
        uploadFiles = filePathCallback;
        Intent i = fileChooserParams.createIntent();
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); // 設(shè)置多選
        _m.startActivityForResult(Intent.createChooser(i, "Image Chooser"), CHOOSER_REQUEST);
        return true;
    }

    // 第二種方式(過濾文件格式)
    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
                                     FileChooserParams fileChooserParams) {
        uploadFiles = filePathCallback;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
        i.setType("*/*"); // 設(shè)置文件類型
        String[] mimeTypes = { "image/*,audio/*,video/*,*/*" };
        i.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); // 設(shè)置多種類型
        i.addCategory(Intent.CATEGORY_OPENABLE);
        _m.startActivityForResult(Intent.createChooser(i, "Image Chooser"), CHOOSER_REQUEST);
        return true;
    }

    // 文件選擇回調(diào)(在 MainActivity.java 的 onActivityResult中調(diào)用此方法)
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public void onActivityResultFileChooser(int requestCode, int resultCode, Intent intent) {
        if (requestCode != CHOOSER_REQUEST || uploadFiles == null)
            return;
        Uri[] results = null;
        if (resultCode == Activity.RESULT_OK) {
            if (intent != null) {
                String dataString = intent.getDataString();
                ClipData clipData = intent.getClipData();
                if (clipData != null) {
                    results = new Uri[clipData.getItemCount()];
                    for (int i = 0; i < clipData.getItemCount(); i++) {
                        ClipData.Item item = clipData.getItemAt(i);
                        results[i] = item.getUri();
                    }
                }
                if (dataString != null)
                    results = new Uri[]{Uri.parse(dataString)};
            }
        }
        uploadFiles.onReceiveValue(results);
        uploadFiles = null;
    }
}

2、喚出自定義文件管理器

2.1 使用第三方插件

這里使用 AndroidFilePicker 插件可自定義文件管理器,見詳細(xì)使用

(1)添加依賴

在項(xiàng)目 build.gradle 配置文件添加倉庫:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

在子模塊(app)的配置文件添加依賴:

dependencies {
    implementation 'me.rosuh:AndroidFilePicker:0.8.2'
}

此庫需要一個權(quán)限:

android.permission.READ_EXTERNAL_STORAGE

如果您沒有提前授予,這個庫會自動申請?jiān)摍?quán)限的。

修改上文 WVChromeClient 類中的 onShowFileChooser() 方法:

private ValueCallback<Uri[]> uploadFiles = null;
// 重寫選擇文件
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
                                    FileChooserParams fileChooserParams) {
    uploadFiles = filePathCallback;
    String uDiskUrl = getUDisk(); // 檢測U盤
    if(uDiskUrl != null) {
        showSingleAlertDialog(uDiskUrl); // 彈出選擇框
    } else {
        showFilePickerManager(""); // 直接打開本地路徑,若無需支持U盤則可以直接調(diào)用此方法喚出自定義文件管理器
    }
    return true;
}

// 判斷是否存在U盤
private String getUDisk() {
    String path = "/mnt/usb/"; // u盤路徑
    File storage = new File(path);
    File[] files = storage.listFiles();
    if(files != null && files.length != 0) {
        return path + files[0].getName() + "/";
    }
    return null;
}

// 打開文件管理器
private void showFilePickerManager(String path) {
    FilePickerManager
            .from((Activity) context) // context 為上文實(shí)例化 WVChromeClient 類時傳入
            .setCustomRootPath(path)
            .forResult(CHOOSER_REQUEST);
}

private int checkedItem = 0;
private boolean isNotOK = true;
// 選擇框
private void showSingleAlertDialog(String path) {
    String[] items = {"本地存儲", "U盤"};
    AlertDialog.Builder alertBuilder = new AlertDialog.Builder(context);
    alertBuilder.setTitle("請選擇");
    alertBuilder.setSingleChoiceItems(items, 0, (dialogInterface, i) -> {
        checkedItem = i;
    });

    alertBuilder.setPositiveButton("確定", (dialogInterface, i) -> {
        isNotOK = false;
        dialogInterface.dismiss();
        String paths = "";
        if(checkedItem == 1) {
            paths = path; // 當(dāng)前選擇U盤,默認(rèn)為本地存儲
        }
        showFilePickerManager(paths);
    });

    alertBuilder.setNegativeButton("取消", (dialogInterface, i) -> dialogInterface.dismiss());

    alertBuilder.setOnDismissListener(dialog -> {
        if(isNotOK) { // 若沒有選擇確定按鈕則取消文件上傳
            uploadFiles.onReceiveValue(null);
            uploadFiles = null;
        }
    });
    alertBuilder.show();
}

// 文件選擇回調(diào)
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void onActivityResultFileChooser(int requestCode, int resultCode, Intent intent) {
    if (requestCode != Config.CHOOSER_REQUEST_CODE || uploadFiles == null)
        return;
    Uri[] results = null;
    if (resultCode == Activity.RESULT_OK) {
        List<String> list = FilePickerManager.obtainData(); // 取到選擇的文件列表
        results = new Uri[list.size()];
        for (int i = 0; i < list.size(); i++) {
            String item = list.get(i);
            Uri uri = getUriForFile(new File(item));
            results[i] = uri;
        }
    }
    uploadFiles.onReceiveValue(results);
    uploadFiles = null;
    isNotOK = true;
}

// File 轉(zhuǎn) Uri
private Uri getUriForFile(File file) {
    String packageName = getPackage(context).packageName;
    Uri contentUri = FileProvider.getUriForFile(context,packageName+".fileProvider", file); // 需要 FileProvider,詳細(xì)使用見下文
    return contentUri;
}

// 獲取當(dāng)前包名
public static PackageInfo getPackage(Context context) {
    PackageManager manager = context.getPackageManager();
    try {
        PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
        return  info;
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
        return null;
    }
}

FileProvider 使用詳見

2.2 手寫文件管理頁面

手寫文件管理頁面有兩個個步驟:獲取文件列表、展示文件列表

(1)獲取文件列表

按一般文件管理器大致有幾個目錄:文檔、音頻、視頻、圖片、下載、所有文件目錄

public class FileManage {
    private Context _c;
    private static final String TAG = "FileManage:";
    public FileManage(Context context) {
        _c = context;
    }

    // 獲取視頻
    public JSONObject getVideos() { // 這里返回一個 JSONObject,返回格式可以自行定義
        Cursor c = null;
        JSONArray array = new JSONArray();
        try {
            Log.e(TAG,MediaStore.Video.Media.EXTERNAL_CONTENT_URI.toString());
            c = _c.getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Video.Media.DEFAULT_SORT_ORDER);
            while (c.moveToNext()) {
                String path = c.getString(c.getColumnIndexOrThrow(MediaStore.Video.Media.DATA));// 路徑
                if (!new File(path).exists()) {
                    continue;
                }

                int id = c.getInt(c.getColumnIndexOrThrow(MediaStore.Video.Media._ID));// 視頻的id
                String name = c.getString(c.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME)); // 視頻名稱
                String resolution = c.getString(c.getColumnIndexOrThrow(MediaStore.Video.Media.RESOLUTION)); //分辨率
                long size = c.getLong(c.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE));// 大小
                long duration = c.getLong(c.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION));// 時長
                long date = c.getLong(c.getColumnIndexOrThrow(MediaStore.Video.Media.DATE_MODIFIED));//修改時間

                JSONObject obj = new JSONObject();
                obj.put("id",id);
                obj.put("name",name);
                obj.put("url",path);
                obj.put("resolution",resolution);
                obj.put("size",size);
                obj.put("duration",duration);
                obj.put("time",getDateToString(date*1000));
                obj.put("timestamp", date*1000);
                array.put(obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (c != null) {
                c.close();
            }
        }
        File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES);
        JSONObject obj = new JSONObject();
        try {
            obj.put("name","視頻");
            obj.put("svg","#icon-file_video");
            obj.put("url", file.getPath());
            if(array != null && array.length() != 0) {
                obj.put("child", array);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return obj;
    }

    // 獲取音頻
    public JSONObject getMusics() {
        Cursor c = null;
        JSONArray array = new JSONArray();
        try {
            c = _c.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,
                    MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
            while (c.moveToNext()) {
                String path = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));// 路徑
                if (!new File(path).exists()) {
                    continue;
                }

                int id = c.getInt(c.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)); // 歌曲的id
                String name = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME)); // 歌曲名
                String album = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM)); // 專輯
                String artist = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)); // 作者
                long size = c.getLong(c.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE));// 大小
                int duration = c.getInt(c.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION));// 時長
                long date = c.getLong(c.getColumnIndexOrThrow(MediaStore.Audio.Media.DATE_MODIFIED));//修改時間
                int albumId = c.getInt(c.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID));

                JSONObject obj = new JSONObject();
                obj.put("id",id);
                obj.put("name",name);
                obj.put("url",path);
                obj.put("album",album);
                obj.put("artist",artist);
                obj.put("size",size);
                obj.put("duration",duration);
                obj.put("time",getDateToString(date*1000));
                obj.put("timestamp", date*1000);
                obj.put("albumId",albumId);
                array.put(obj);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (c != null) {
                c.close();
            }
        }
        File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
        JSONObject obj = new JSONObject();
        try {
            obj.put("name","音頻");
            obj.put("svg","#icon-file_music");
            obj.put("url", file.getPath());
            if(array != null && array.length() != 0) {
                obj.put("child", array);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return obj;
    }

    // 獲取圖片
    public JSONObject getImages() {
        // 掃描圖片
        Cursor c = null;
        JSONArray array = new JSONArray();
        try {
            c = _c.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null,
                    MediaStore.Images.Media.MIME_TYPE + "= ? or " + MediaStore.Images.Media.MIME_TYPE + "= ?",
                    new String[]{"image/jpeg", "image/png"}, MediaStore.Images.Media.DATE_MODIFIED);
            while (c.moveToNext()) {
                String path = c.getString(c.getColumnIndexOrThrow(MediaStore.Images.Media.DATA));// 路徑
//                @SuppressLint("Range") String path = c.getString(c.getColumnIndex(MediaStore.Images.Media.DATA));// 路徑
                File parentFile = new File(path).getParentFile();
                if (parentFile == null)
                    continue;
                int id = c.getInt(c.getColumnIndexOrThrow(MediaStore.Images.Media._ID)); // 圖片的id
                String name = c.getString(c.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)); // 圖片名
                long size = c.getLong(c.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE));// 大小
                long date = c.getLong(c.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_MODIFIED));//修改時間

                JSONObject obj = new JSONObject();
                obj.put("id",id);
                obj.put("name",name);
                obj.put("url",path);
                obj.put("size",size);
                obj.put("time",getDateToString(date*1000));
                obj.put("timestamp", date*1000);
                array.put(obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (c != null) {
                c.close();
            }
        }
        File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        JSONObject obj = new JSONObject();
        try {
            obj.put("name","圖片");
            obj.put("svg","#icon-file_img");
            obj.put("url", file.getPath());
            if(array != null && array.length() != 0) {
                obj.put("child", array);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return obj;
    }

    // 獲取文檔
    public JSONObject getDocuments() {
        File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
        JSONObject obj = getFileList(file);
        try {
            obj.put("name","文檔");
            obj.put("svg","#icon-file1");
            obj.put("isFilter",true);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return obj;
    }

    // 獲取下載
    public JSONObject getDownloads() {
        File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
        JSONObject obj = getFileList(file);
        try {
            obj.put("name","下載");
            obj.put("svg","#icon-file_download");
            obj.put("isFilter",true);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return obj;
    }

    // 獲取根文件
    public JSONObject getRoots() {
        File file = Environment.getExternalStorageDirectory();
        return getFileList(file);
    }

    // 獲取本地文件
    public JSONObject getLocalStore() {
        JSONObject obj = getRoots();
        try {
            obj.put("name","本地");
            JSONArray array = new JSONArray();

            array.put(getDocuments());
            array.put(getMusics());
            array.put(getVideos());
            array.put(getImages());
            array.put(getDownloads());

            obj.put("typeList", array);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return obj;
    }

    // 獲取U盤文件
    public JSONObject getDiskFiles() {
        JSONObject obj = null;
        String path = "/mnt/usb/";
        File file = new File(path);
        File[] files = file.listFiles();
        if(files != null && files.length != 0) {
            obj = getFileList(file);
            try {
                obj.put("name","U盤");
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        return obj;
    }

    // 獲取目錄文件下所有文件列表
    private JSONObject getFileList(File file) {
        JSONObject message = new JSONObject();
        try {
            if(file.exists()){
                message.put("name", file.getName());
                message.put("url", file.getPath());
                message.put("timestamp", file.lastModified());
                message.put("time", getDateToString(file.lastModified()));
                message.put("size", file.length());
                File[] list = file.listFiles();
                if(list != null && list.length != 0) {
                    List fileList = Arrays.asList(list);
                    Collections.sort(fileList, (Comparator<File>) (o1, o2) -> {
                        if (o1.isDirectory() && o2.isFile())
                            return -1;
                        if (o1.isFile() && o2.isDirectory())
                            return 1;
                        return o1.getName().compareTo(o2.getName());
                    });
                    JSONArray arr = new JSONArray();
                    for(File item : list) {
                        arr.put(getFileList(item));
                    }
                    message.put("child", arr);
                } else {
                    if(file.isDirectory()) {
                        message.put("child",new JSONArray());
                    }
                }
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return message;
    }

    // File 轉(zhuǎn) Uri
    public Uri getUriForFile(File file) {
        String packageName = GetDevice.getPackage(_c).packageName;
        Uri contentUri = FileProvider.getUriForFile(_c,packageName+".fileProvider", file);
        return contentUri;
    }

    private String getDateToString(long milSecond) {
        String pattern = "yyyy-MM-dd HH:mm:ss";
        Date date = new Date(milSecond);
        SimpleDateFormat format = new SimpleDateFormat(pattern);
        return format.format(date);
    }

    // 獲取視頻縮略圖
    public Bitmap getVideoThumbnail(int id) {
        Bitmap bitmap = null;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inDither = false;
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        bitmap = MediaStore.Video.Thumbnails.getThumbnail(_c.getContentResolver(), id, MediaStore.Images.Thumbnails.MICRO_KIND, options);
        return bitmap;
    }
}

(2)展示文件列表

通過上面的 FileManage 里面的方法就可以獲取到 Android 系統(tǒng)里面大部分文件列表了

展示可以使用 Android 的 Activity 布局展示,這里使用的是 H5 寫的文件管理,實(shí)現(xiàn)邏輯一致(取到文件列表->展示->選擇文件->得到文件URI)。

在 WChromeClient.java 中編寫

private ValueCallback<Uri[]> uploadFiles = null;
// 重寫選擇文件
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
                                    FileChooserParams fileChooserParams) {
    uploadFiles = filePathCallback; // 取到 filePathCallback 回調(diào)之后不做處理
    return true;
}

// 文件選擇回調(diào)(這個方法提供給js調(diào)用)
public void resultFileChoose(String json) { // 參數(shù)是前端js選擇了一項(xiàng)或多項(xiàng)的列表
    Uri[] results = null;
    try {
        JSONArray jsonArray = new JSONArray(json);
        results = new Uri[jsonArray.length()];
        for(int i=0; i<jsonArray.length(); i++) {
            JSONObject obj = jsonArray.getJSONObject(i);
            String url = obj.getString("url");
            File file = new File(url);
            results[i] = getUriForFile(file);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    receiveFile(results);
}

public void receiveFile(Uri[] results) {
    uploadFiles.onReceiveValue(results);
    uploadFiles = null;
}

提供給js的方法:

FileManage fileManage = new FileManage(context); // context 就是 MainActivity 的 this
// 獲取本地文件列表
@JavascriptInterface
public String getLocalStore() {
    String str = fileManage.getLocalStore().toString();
    return str;
}

// 獲取U盤文件列表
@JavascriptInterface
public String getDiskFiles() {
    JSONObject obj = fileManage.getDiskFiles();
    if(obj!=null) {
        String str = fileManage.getDiskFiles().toString();
        return str;
    }else{
        return "null";
    }
}

// 選擇文件
@JavascriptInterface
public void getFileList(String json) {
    wChromeClient.resultFileChoose(json); // resultFileChoose 就是前面 WChromeClient.java 里面的
}

// 取消選擇(注意取消選擇文件必須置空回調(diào))
@JavascriptInterface
public void cancelFile() { wChromeClient.receiveFile(null); }

js如何調(diào)用:

// 獲取設(shè)備根目錄
toAndroid({ methods: 'getLocalStore' }, val => {
    const json = JSON.parse(val);
    this.fileList.push(json); // 文件列表
});

// 獲取設(shè)備U盤目錄
toAndroid({ methods: 'getDiskFiles' }, val => {
    if (val !== 'null') {
    const json = JSON.parse(val);
    this.fileList.push(json);
    }
});

// 調(diào)用安卓方法
export default function toAndroid(infor, success, error) {
  try {
    const { methods, params } = infor;
    let data = null;
    params
      ? (data = window.jsWebView[methods](params))
      : (data = window.jsWebView[methods]());
    success && success(data);
  } catch (e) {
    error && error();
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容