Android webview 實(shí)現(xiàn)h5的input type="file"選擇圖片調(diào)用系統(tǒng)相冊(cè)/相機(jī)并進(jìn)行圖片壓縮功能

一乌助、引言

webview怎么實(shí)現(xiàn)web的<input type="file" />選擇圖片功能萌京,如何讓h5通過webview調(diào)用系統(tǒng)相冊(cè)和相機(jī)焰轻,并在圖片傳回h5的時(shí)已經(jīng)將圖片做了壓縮處理数苫?本篇就是解決這方面的問題聪舒。

這邊h5 的<input type="file" />上傳文件只是限定于圖片類型,不需要pdf虐急、txt等其他類型箱残,如果要寫一個(gè)普通的不限定于圖片的上傳文件功能可以參考http://www.reibang.com/p/b0f1fdbfd502

二止吁、Webview實(shí)現(xiàn)

Webview要調(diào)用系統(tǒng)相冊(cè)/相機(jī)被辑,需要setWebChromeClient并重寫WebChromeClient的方法。

mWebView.setWebChromeClient(new WebChromeClient(){
            
            // For Android < 3.0
            public void openFileChooser(ValueCallback<Uri> valueCallback) {
                mUploadCallBack = valueCallback;
                showFileChooser();
            }

            // For Android  >= 3.0
            public void openFileChooser(ValueCallback valueCallback, String acceptType) {
                mUploadCallBack = valueCallback;
                showFileChooser();
            }

            //For Android  >= 4.1
            public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
                mUploadCallBack = valueCallback;
                showFileChooser();
            }

            // For Android >= 5.0
            @Override
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
                mUploadCallBackAboveL = filePathCallback;
                showFileChooser();
                return true;
            }
        });
    /**
     * 打開選擇圖片/相機(jī)
     */
    private void showFileChooser() {

        Intent intent1 = new Intent(Intent.ACTION_PICK, null);
        intent1.setDataAndType(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
//        Intent intent1 = new Intent(Intent.ACTION_GET_CONTENT);
//        intent1.addCategory(Intent.CATEGORY_OPENABLE);
//        intent1.setType("image/*");

        Intent intent2 = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        mCameraFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator +
                System.currentTimeMillis() + ".jpg";
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            // android7.0注意uri的獲取方式改變
            Uri photoOutputUri = FileProvider.getUriForFile(
                    MainActivity.this,
                    BuildConfig.APPLICATION_ID + ".fileProvider",
                    new File(mCameraFilePath));
            intent2.putExtra(MediaStore.EXTRA_OUTPUT, photoOutputUri);
        } else {
            intent2.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mCameraFilePath)));
        }

        Intent chooser = new Intent(Intent.ACTION_CHOOSER);
        chooser.putExtra(Intent.EXTRA_TITLE, "File Chooser");
        chooser.putExtra(Intent.EXTRA_INTENT,intent1);
        chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{intent2});
        startActivityForResult(chooser, REQUEST_CODE_FILE_CHOOSER);
    }

然后敬惦,在onActivityResult里處理獲取到的圖片盼理。

 @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_FILE_CHOOSER) {
            Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
            // 壓縮到多少寬度以內(nèi)
            int maxW = 1000;
            // 壓縮到多少大小以內(nèi),1024kb
            int maxSize = 1024;
            if (result == null) {
                // 看是否從相機(jī)返回
                File cameraFile = new File(mCameraFilePath);
                if (cameraFile.exists()) {
                    result = Uri.fromFile(cameraFile);
                    sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
                }
            }
            if (result != null) {
                // 根據(jù)uri獲取路徑
                String path = FileUtils.getPath(this, result);
                if (!TextUtils.isEmpty(path)) {
                    File f = new File(path);
                    if (f.exists() && f.isFile()) {
                        // 按大小和尺寸壓縮圖片
                        Bitmap b = getCompressBitmap(path, maxW, maxW, maxSize);
                        String basePath = Environment.getExternalStorageDirectory().getAbsolutePath();
                        String compressPath = basePath + File.separator + "photos" + File.separator
                                + System.currentTimeMillis() + ".jpg";
                        // 壓縮完保存在文件里
                        if (saveBitmapToFile(b, compressPath)) {
                            Uri newUri = null;
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                                newUri = FileProvider.getUriForFile(
                                        MainActivity.this,
                                        BuildConfig.APPLICATION_ID + ".fileProvider",
                                        new File(compressPath));
                            } else {
                                newUri = Uri.fromFile(new File(compressPath));
                            }

                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                                if (mUploadCallBackAboveL != null) {
                                    if (newUri != null) {
                                        mUploadCallBackAboveL.onReceiveValue(new Uri[]{newUri});
                                        mUploadCallBackAboveL = null;
                                        return;
                                    }

                                }
                            } else if (mUploadCallBack != null) {
                                if (newUri != null) {
                                    mUploadCallBack.onReceiveValue(newUri);
                                    mUploadCallBack = null;
                                    return;
                                }
                            }
                        }
                    }
                }
            }
            clearUploadMessage();
            return;
        }
    }

如果沒有圖片返回給h5,記得要執(zhí)行下面代碼俄删,避免h5下次點(diǎn)擊選擇圖片時(shí)無(wú)法響應(yīng)宏怔。

/**
     * webview沒有選擇圖片也要傳null,防止下次無(wú)法執(zhí)行
     */
    private void clearUploadMessage(){
        if (mUploadCallBackAboveL != null) {
            mUploadCallBackAboveL.onReceiveValue(null);
            mUploadCallBackAboveL = null;
        }
        if (mUploadCallBack != null) {
            mUploadCallBack.onReceiveValue(null);
            mUploadCallBack = null;
        }
    }

相關(guān)的壓縮和保存圖片代碼如下:

 /**
     * 根據(jù)路徑獲取bitmap(壓縮后)
     *
     * @param srcPath 圖片路徑
     * @param width   最大寬(壓縮完可能會(huì)大于這個(gè)畴椰,這邊只是作為大概限制举哟,避免內(nèi)存溢出)
     * @param height  最大高(壓縮完可能會(huì)大于這個(gè),這邊只是作為大概限制迅矛,避免內(nèi)存溢出)
     * @param size    圖片大小妨猩,單位kb
     * @return 返回壓縮后的bitmap
     */
    public static Bitmap getCompressBitmap(String srcPath, float width, float height, int size) {
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        // 開始讀入圖片,此時(shí)把options.inJustDecodeBounds 設(shè)回true了
        newOpts.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(srcPath, newOpts);

        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        int scaleW = (int) (w / width);
        int scaleH = (int) (h / height);
        int scale = scaleW < scaleH ? scaleH : scaleW;
        if (scale <= 1) {
            scale = 1;
        }
        newOpts.inSampleSize = scale;// 設(shè)置縮放比例
        // 重新讀入圖片秽褒,注意此時(shí)已經(jīng)把options.inJustDecodeBounds 設(shè)回false了
        Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
        // 壓縮好比例大小后再進(jìn)行質(zhì)量壓縮
        return compressImage(bitmap, size);
    }

    /**
     * 圖片質(zhì)量壓縮
     *
     * @param image 傳入的bitmap
     * @param size  壓縮到多大壶硅,單位kb
     * @return 返回壓縮完的bitmap
     */
    public static Bitmap compressImage(Bitmap image, int size) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 質(zhì)量壓縮方法,這里100表示不壓縮销斟,把壓縮后的數(shù)據(jù)存放到baos中
        int options = 100;
        image.compress(Bitmap.CompressFormat.JPEG, options, baos);
        // 循環(huán)判斷如果壓縮后圖片是否大于size,大于繼續(xù)壓縮
        while (baos.toByteArray().length / 1024 > size) {
            // 重置baos即清空baos
            baos.reset();
            // 每次都減少10
            options -= 10;
            // 這里壓縮options%庐椒,把壓縮后的數(shù)據(jù)存放到baos中
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);
        }
        // 把壓縮后的數(shù)據(jù)baos存放到ByteArrayInputStream中
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
        // 把ByteArrayInputStream數(shù)據(jù)生成圖片
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);
        return bitmap;
    }

    /**
     * bitmap保存為文件
     *
     * @param bm       bitmap
     * @param filePath 文件路徑
     * @return 返回保存結(jié)果 true:成功,false:失敗
     */
    public static boolean saveBitmapToFile(Bitmap bm, String filePath) {
        try {
            File file = new File(filePath);
            file.deleteOnExit();
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
            boolean b = false;
            if (filePath.toLowerCase().endsWith(".png")) {
                b = bm.compress(Bitmap.CompressFormat.PNG, 100, bos);
            } else {
                b = bm.compress(Bitmap.CompressFormat.JPEG, 100, bos);
            }
            bos.flush();
            bos.close();
            return b;
        } catch (FileNotFoundException e) {
        } catch (IOException e) {
        }
        return false;
    }

對(duì)于根據(jù)uri獲取圖片路徑的代碼也比較關(guān)鍵蚂踊。相關(guān)代碼可以參考http://www.reibang.com/p/25c35da68db2

如果你的應(yīng)用混淆了要注意下,openFileChooser方法并不是WebChromeClient的對(duì)外開放的方法约谈,因此這個(gè)方法會(huì)被混淆,解決辦法也比較簡(jiǎn)單,只需要在混淆文件里控制一下即可:

-keepclassmembers class * extends android.webkit.WebChromeClient{
    public void openFileChooser(...);
}

好了棱诱,這樣就可以讓webview調(diào)用原生相機(jī)和相冊(cè)選擇圖片泼橘,并對(duì)圖片做壓縮處理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末迈勋,一起剝皮案震驚了整個(gè)濱河市炬灭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌靡菇,老刑警劉巖重归,帶你破解...
    沈念sama閱讀 221,331評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異厦凤,居然都是意外死亡鼻吮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,372評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門较鼓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)椎木,“玉大人,你說(shuō)我怎么就攤上這事笨腥⊥夭福” “怎么了?”我有些...
    開封第一講書人閱讀 167,755評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵脖母,是天一觀的道長(zhǎng)士鸥。 經(jīng)常有香客問我,道長(zhǎng)谆级,這世上最難降的妖魔是什么烤礁? 我笑而不...
    開封第一講書人閱讀 59,528評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮肥照,結(jié)果婚禮上脚仔,老公的妹妹穿的比我還像新娘。我一直安慰自己舆绎,他們只是感情好鲤脏,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,526評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吕朵,像睡著了一般猎醇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上努溃,一...
    開封第一講書人閱讀 52,166評(píng)論 1 308
  • 那天硫嘶,我揣著相機(jī)與錄音,去河邊找鬼梧税。 笑死沦疾,一個(gè)胖子當(dāng)著我的面吹牛称近,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播哮塞,決...
    沈念sama閱讀 40,768評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼刨秆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了彻桃?” 一聲冷哼從身側(cè)響起坛善,我...
    開封第一講書人閱讀 39,664評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤晾蜘,失蹤者是張志新(化名)和其女友劉穎邻眷,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體剔交,經(jīng)...
    沈念sama閱讀 46,205評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肆饶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,290評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了岖常。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片驯镊。...
    茶點(diǎn)故事閱讀 40,435評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖竭鞍,靈堂內(nèi)的尸體忽然破棺而出板惑,到底是詐尸還是另有隱情,我是刑警寧澤偎快,帶...
    沈念sama閱讀 36,126評(píng)論 5 349
  • 正文 年R本政府宣布冯乘,位于F島的核電站,受9級(jí)特大地震影響晒夹,放射性物質(zhì)發(fā)生泄漏裆馒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,804評(píng)論 3 333
  • 文/蒙蒙 一丐怯、第九天 我趴在偏房一處隱蔽的房頂上張望喷好。 院中可真熱鬧,春花似錦读跷、人聲如沸梗搅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,276評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)无切。三九已至,卻和暖如春钦铺,著一層夾襖步出監(jiān)牢的瞬間订雾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工矛洞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留洼哎,地道東北人烫映。 一個(gè)月前我還...
    沈念sama閱讀 48,818評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像噩峦,于是被迫代替她去往敵國(guó)和親锭沟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,442評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容