【问题标题】:File Upload in WebViewWebView 中的文件上传
【发布时间】:2011-08-19 22:13:10
【问题描述】:

自从最近几次以来,我一直在努力从 WebView 上传文件 几天了,一点进展都没有。我用谷歌搜索并实施了所有建议 解决方案,但没有一个有效,例如:建议的解决方案here, 等等。

问题:我有一个带有以下代码的 HTML 页面来上传文件。 它在 Firefox 等桌面浏览器和内置浏览器中运行良好 模拟器/ AVD,即当我点击“浏览...”按钮呈现 元素,浏览器打开一个对话框 我可以在其中选择要上传的文件的框。

但是,在 android 3.0 模拟器/AVD 中,当我点击“选择 文件”,没有任何反应,没有打开文件对话框!!!

<form method="POST" enctype="multipart/form-data">
File to upload: <input type="file" name="uploadfile">&nbsp;&nbsp;
<input type="submit" value="Press to Upload..."> to upload the file!
</form>

谁能尽早提出一个可能的解决方案。

【问题讨论】:

标签: android android-emulator webview


【解决方案1】:

这是适用于所有 android 版本的完整解决方案,我也遇到了困难。

public class MyWb extends Activity {
/** Called when the activity is first created. */

WebView web;
ProgressBar progressBar;

private ValueCallback<Uri> mUploadMessage;  
 private final static int FILECHOOSER_RESULTCODE=1;  

 @Override  
 protected void onActivityResult(int requestCode, int resultCode,  
                                    Intent intent) {  
  if(requestCode==FILECHOOSER_RESULTCODE)  
  {  
   if (null == mUploadMessage) return;  
            Uri result = intent == null || resultCode != RESULT_OK ? null  
                    : intent.getData();  
            mUploadMessage.onReceiveValue(result);  
            mUploadMessage = null;  
  }
  }  

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    web = (WebView) findViewById(R.id.webview01);
    progressBar = (ProgressBar) findViewById(R.id.progressBar1);

    web = new WebView(this);  
    web.getSettings().setJavaScriptEnabled(true);
    web.loadUrl("http://www.script-tutorials.com/demos/199/index.html");
    web.setWebViewClient(new myWebClient());
    web.setWebChromeClient(new WebChromeClient()  
    {  
           //The undocumented magic method override  
           //Eclipse will swear at you if you try to put @Override here  
        // For Android 3.0+
        public void openFileChooser(ValueCallback<Uri> uploadMsg) {  

            mUploadMessage = uploadMsg;  
            Intent i = new Intent(Intent.ACTION_GET_CONTENT);  
            i.addCategory(Intent.CATEGORY_OPENABLE);  
            i.setType("image/*");  
            MyWb.this.startActivityForResult(Intent.createChooser(i,"File Chooser"), FILECHOOSER_RESULTCODE);  

           }

        // For Android 3.0+
           public void openFileChooser( ValueCallback uploadMsg, String acceptType ) {
           mUploadMessage = uploadMsg;
           Intent i = new Intent(Intent.ACTION_GET_CONTENT);
           i.addCategory(Intent.CATEGORY_OPENABLE);
           i.setType("*/*");
           MyWb.this.startActivityForResult(
           Intent.createChooser(i, "File Browser"),
           FILECHOOSER_RESULTCODE);
           }

        //For Android 4.1
           public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
               mUploadMessage = uploadMsg;  
               Intent i = new Intent(Intent.ACTION_GET_CONTENT);  
               i.addCategory(Intent.CATEGORY_OPENABLE);  
               i.setType("image/*");  
               MyWb.this.startActivityForResult( Intent.createChooser( i, "File Chooser" ), MyWb.FILECHOOSER_RESULTCODE );

           }

    });  


    setContentView(web);  


}

public class myWebClient extends WebViewClient
{
    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        // TODO Auto-generated method stub
        super.onPageStarted(view, url, favicon);
    }

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        // TODO Auto-generated method stub

        view.loadUrl(url);
        return true;

    }

    @Override
    public void onPageFinished(WebView view, String url) {
        // TODO Auto-generated method stub
        super.onPageFinished(view, url);

        progressBar.setVisibility(View.GONE);
    }
}

//flipscreen not loading again
@Override
public void onConfigurationChanged(Configuration newConfig){        
    super.onConfigurationChanged(newConfig);
}

// To handle "Back" key press event for WebView to go back to previous screen.
/*@Override
public boolean onKeyDown(int keyCode, KeyEvent event) 
{
    if ((keyCode == KeyEvent.KEYCODE_BACK) && web.canGoBack()) {
        web.goBack();
        return true;
    }
    return super.onKeyDown(keyCode, event);
}*/
}

我还想补充一点,像这个例子中的“上传页面”,不能在

更新

请找到棒棒糖设备的解决方案here 并感谢gauntface

更新 2

oreo herethis is more advanced version 之前为所有安卓设备提供完整的解决方案,你应该看看它,也许它可以帮助。

【讨论】:

  • 非常感谢您节省我的时间!
  • @hifarrer 你是救生员。这很好用。另外,对于我们这些菜鸟来说,完整的代码有助于理解放置及其工作原理,而不是仅仅粘贴一堆函数,然后我们不知道在哪里放置和使用它们。感谢您的详细回复。
  • 这种方法混淆后如何处理?我使用相同的方法没有混淆它工作正常但是当我的应用程序使用混淆部署时,这个方法永远不会被调用。我已将此方法保留在 proguard 中,有人看到过这个问题吗?
  • 它可以工作,但是为什么值没有按应有的方式更新?这是我在 Eclipse Android API17 模拟器上的测试。
  • progressBar 在那里似乎没有使用。我只能看到它的隐藏,但看不到它被激活的地方。我认为您需要为此添加 onProgressChanged(...) 到自定义 WebChromeClient 变体中。 (类似getWindow().setFeatureInt( Window.FEATURE_PROGRESS, Window.PROGRESS_VISIBILITY_ON); 变成onCreate。)
【解决方案2】:

从 HONEYCOMB (API 11) 到 Android 11 的工作方法

由 swati vishnoi 更新,这也适用于 Pie 及以上版本

static WebView mWebView;
private ValueCallback<Uri> mUploadMessage;
public ValueCallback<Uri[]> uploadMessage;
public static final int REQUEST_SELECT_FILE = 100;
private final static int FILECHOOSER_RESULTCODE = 1;

修改onActivityResult()

@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent)
{
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
    {
        if (requestCode == REQUEST_SELECT_FILE)
        {
            if (uploadMessage == null)
                return;
            uploadMessage.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, intent));
            uploadMessage = null;
        }
    }
    else if (requestCode == FILECHOOSER_RESULTCODE)
    {
        if (null == mUploadMessage)
            return;
    // Use MainActivity.RESULT_OK if you're implementing WebView inside Fragment
    // Use RESULT_OK only if you're implementing WebView inside an Activity
        Uri result = intent == null || resultCode != MainActivity.RESULT_OK ? null : intent.getData();
        mUploadMessage.onReceiveValue(result);
        mUploadMessage = null;
    }
    else
        Toast.makeText(getActivity().getApplicationContext(), "Failed to Upload Image", Toast.LENGTH_LONG).show();
}

现在在onCreate()onCreateView()粘贴以下代码

    WebSettings mWebSettings = mWebView.getSettings();
    mWebSettings.setJavaScriptEnabled(true);
    mWebSettings.setSupportZoom(false);
    mWebSettings.setAllowFileAccess(true);
    mWebSettings.setAllowContentAccess(true);

mWebView.setWebChromeClient(new WebChromeClient()
{
    // For 3.0+ Devices (Start)
    // onActivityResult attached before constructor
    protected void openFileChooser(ValueCallback uploadMsg, String acceptType)
    {
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        startActivityForResult(Intent.createChooser(i, "File Browser"), FILECHOOSER_RESULTCODE);
    }
   

    // For Lollipop 5.0+ Devices
    public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams)
    {
        if (uploadMessage != null) {
            uploadMessage.onReceiveValue(null);
            uploadMessage = null;
        }

        uploadMessage = filePathCallback;

        Intent intent = fileChooserParams.createIntent();
        try
        {
            startActivityForResult(intent, REQUEST_SELECT_FILE);
        } catch (ActivityNotFoundException e)
        {
            uploadMessage = null;
            Toast.makeText(getActivity().getApplicationContext(), "Cannot Open File Chooser", Toast.LENGTH_LONG).show();
            return false;
        }
        return true;
    }
    
    //For Android 4.1 only
    protected void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)
    {
        mUploadMessage = uploadMsg;
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("image/*");
        startActivityForResult(Intent.createChooser(intent, "File Browser"), FILECHOOSER_RESULTCODE);
    }

    protected void openFileChooser(ValueCallback<Uri> uploadMsg)
    {
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
    }
});

【讨论】:

  • 解决了我的问题。谢谢。
  • 感谢您的完整代码。它在 except kitkat Os 中工作正常。你能告诉我如何在 kitkat 中提供支持吗?谢谢
  • 根据评论here,在应用标签(清单文件)中添加android:usesCleartextTraffic="true",使其在Pie中工作。
  • @NightFury 如果您尝试访问 http 链接,您必须这样做。改用 https
  • 在 9.0 上工作得非常好。
【解决方案3】:

这是我发现的唯一可行的解​​决方案!

WebView webview;

private ValueCallback<Uri> mUploadMessage;
private final static int FILECHOOSER_RESULTCODE = 1;

@Override
protected void onActivityResult(int requestCode, int resultCode,
        Intent intent) {
    if (requestCode == FILECHOOSER_RESULTCODE) {
        if (null == mUploadMessage)
            return;
        Uri result = intent == null || resultCode != RESULT_OK ? null
                : intent.getData();
        mUploadMessage.onReceiveValue(result);
        mUploadMessage = null;

    }
}

// Next part 

class MyWebChromeClient extends WebChromeClient {
    // The undocumented magic method override
    // Eclipse will swear at you if you try to put @Override here
    public void openFileChooser(ValueCallback<Uri> uploadMsg) {

        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        Cv5appActivity.this.startActivityForResult(
                Intent.createChooser(i, "Image Browser"),
                FILECHOOSER_RESULTCODE);
    }
}

【讨论】:

  • 你能澄清一下我应该把这段代码放在哪里吗?以及我应该如何使用它?
  • 从 4.4 (KitKat) 开始,openFileChooser 不再可用,因此使用它的方法不起作用。
  • @nadeemgc 在我的例子中,我也控制着网络服务器,所以我正在使用不同的 URL 方案(又名“my-app://some/other/arguments”)检测 Android 4.4+对于上传链接,在 shouldOverrideUrlLoading() 中拦截这些 URL,然后显示一个选择器并在本机代码中针对这种情况进行上传。这并不是一个轻量级的解决方法。
  • 我可以在 android 4.4.2 中使用 webview 上传图片吗?
  • @SteveN,你能提供一个 sn-p 吗?太棒了!
【解决方案4】:

在 5.0 Lollipop 中,Google 增加了一个官方方法,WebChromeClient.onShowFileChooser。它们甚至提供了一种自动生成文件选择器意图的方法,以便它使用输入接受 mime 类型。

public class MyWebChromeClient extends WebChromeClient {
        // reference to activity instance. May be unnecessary if your web chrome client is member class.
    private MyActivity activity;

    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
        // make sure there is no existing message
        if (myActivity.uploadMessage != null) {
            myActivity.uploadMessage.onReceiveValue(null);
            myActivity.uploadMessage = null;
        }

        myActivity.uploadMessage = filePathCallback;

        Intent intent = fileChooserParams.createIntent();
        try {
            myActivity.startActivityForResult(intent, MyActivity.REQUEST_SELECT_FILE);
        } catch (ActivityNotFoundException e) {
            myActivity.uploadMessage = null;
            Toast.makeText(myActivity, "Cannot open file chooser", Toast.LENGTH_LONG).show();
            return false;
        }

        return true;
    }
}


public class MyActivity extends ... {
    public static final int REQUEST_SELECT_FILE = 100;
    public ValueCallback<Uri[]> uploadMessage;

    protected void onActivityResult(int requestCode, int resultCode, Intent data){
        if (requestCode == REQUEST_SELECT_FILE) {
                if (uploadMessage == null) return;
                uploadMessage.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));
                uploadMessage = null;
            }
        }
    }
}

对于 KitKat 之前的 Android 版本,其他答案中提到的私有方法有效。我还没有为 KitKat (4.4) 找到好的解决方法。

【讨论】:

  • 在我的情况下,uploadMessage.onReceiveValue() 有效,并且关闭了)
  • 这对我不起作用。我尝试使用您的代码创建一个名为“MyWebChromeClient”的新类,但发现 MyActivity 是一个我无法导入的类...我可以创建自己的名为“MyActivity”的类,但我不知道该放什么进入它。
【解决方案5】:

我发现我需要 3 个接口定义来处理各种版本的 android。

public void openFileChooser(ValueCallback < Uri > uploadMsg) {
  mUploadMessage = uploadMsg;
  Intent i = new Intent(Intent.ACTION_GET_CONTENT);
  i.addCategory(Intent.CATEGORY_OPENABLE);
  i.setType("image/*");
  FreeHealthTrack.this.startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILECHOOSER_RESULTCODE);
}

public void openFileChooser(ValueCallback < Uri > uploadMsg, String acceptType) {
  openFileChooser(uploadMsg);
}

public void openFileChooser(ValueCallback < Uri > uploadMsg, String acceptType, String capture) {
  openFileChooser(uploadMsg);
}

【讨论】:

  • 谢谢,Jelly Bean 需要最后一个,这对我不起作用。
  • 这种方法混淆后如何处理?我使用相同的方法没有混淆它工作正常但是当我的应用程序使用混淆部署时,这个方法永远不会被调用。我已将此方法保留在 proguard 中,有人看到过这个问题吗?
  • 最好是反其道而行之,即把代码放在签名最丰富的最后一个,并利用其参数来选择启动哪种意图。然后让其他两个使用这些参数的默认值调用最后一个。
【解决方案6】:

此解决方案也适用于蜂窝和冰淇淋三明治。似乎 Google 引入了一个很酷的新功能(accept 属性),却忘记实现向后兼容的重载。

protected class CustomWebChromeClient extends WebChromeClient
{
    // For Android 3.0+
    public void openFileChooser( ValueCallback<Uri> uploadMsg, String acceptType ) 
    {  
        context.mUploadMessage = uploadMsg;  
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);  
        i.addCategory(Intent.CATEGORY_OPENABLE);  
        i.setType("image/*");  
        context.startActivityForResult( Intent.createChooser( i, "File Chooser" ), MainActivity.FILECHOOSER_RESULTCODE );  
    }

    // For Android < 3.0
    public void openFileChooser( ValueCallback<Uri> uploadMsg ) 
    {
        openFileChooser( uploadMsg, "" );
    }
}

【讨论】:

  • 你能澄清一下我应该把这门课放在哪里吗?我应该如何使用它?谢谢
  • 将此类放在您活动中的任何位置并使用此代码 webview.setWebChromeClient(new CustomWebChromeClient());
  • 从 4.4 (KitKat) 开始,openFileChooser 不再可用,因此使用它的方法不起作用。
【解决方案7】:

hifarrer 的完整解决方案对我很有帮助。

但是,我遇到了许多其他问题 - 支持其他 mime 类型,列出捕获设备(相机、视频、音频重新编码器),立即打开捕获设备(例如: ).. .

所以,我制作了一个与默认网络浏览器应用程序完全相同的解决方案。

我使用了 android-4.4.3_r1/src/com/android/browser/UploadHandler.java。 (感谢鲁珀特·罗恩斯利)

package org.mospi.agatenativewebview;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.View;
import android.webkit.JsResult;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebSettings.PluginState;
import android.widget.Toast;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        WebView webView = (WebView) findViewById(R.id.webView1);

        initWebView(webView);
        webView.loadUrl("http://google.com"); // TODO input your url

    }

    private final static Object methodInvoke(Object obj, String method, Class<?>[] parameterTypes, Object[] args) {
        try {
            Method m = obj.getClass().getMethod(method, new Class[] { boolean.class });
            m.invoke(obj, args);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    private void initWebView(WebView webView) {

        WebSettings settings = webView.getSettings();

        settings.setJavaScriptEnabled(true);
        settings.setAllowFileAccess(true);
        settings.setDomStorageEnabled(true);
        settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
        settings.setLoadWithOverviewMode(true);
        settings.setUseWideViewPort(true);
        settings.setSupportZoom(true);
        // settings.setPluginsEnabled(true);
        methodInvoke(settings, "setPluginsEnabled", new Class[] { boolean.class }, new Object[] { true });
        // settings.setPluginState(PluginState.ON);
        methodInvoke(settings, "setPluginState", new Class[] { PluginState.class }, new Object[] { PluginState.ON });
        // settings.setPluginsEnabled(true);
        methodInvoke(settings, "setPluginsEnabled", new Class[] { boolean.class }, new Object[] { true });
        // settings.setAllowUniversalAccessFromFileURLs(true);
        methodInvoke(settings, "setAllowUniversalAccessFromFileURLs", new Class[] { boolean.class }, new Object[] { true });
        // settings.setAllowFileAccessFromFileURLs(true);
        methodInvoke(settings, "setAllowFileAccessFromFileURLs", new Class[] { boolean.class }, new Object[] { true });

        webView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
        webView.clearHistory();
        webView.clearFormData();
        webView.clearCache(true);

        webView.setWebChromeClient(new MyWebChromeClient());
        // webView.setDownloadListener(downloadListener);
    }

    UploadHandler mUploadHandler;

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {

        if (requestCode == Controller.FILE_SELECTED) {
            // Chose a file from the file picker.
            if (mUploadHandler != null) {
                mUploadHandler.onResult(resultCode, intent);
            }
        }

        super.onActivityResult(requestCode, resultCode, intent);
    }

    class MyWebChromeClient extends WebChromeClient {
        public MyWebChromeClient() {

        }

        private String getTitleFromUrl(String url) {
            String title = url;
            try {
                URL urlObj = new URL(url);
                String host = urlObj.getHost();
                if (host != null && !host.isEmpty()) {
                    return urlObj.getProtocol() + "://" + host;
                }
                if (url.startsWith("file:")) {
                    String fileName = urlObj.getFile();
                    if (fileName != null && !fileName.isEmpty()) {
                        return fileName;
                    }
                }
            } catch (Exception e) {
                // ignore
            }

            return title;
        }

        @Override
        public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
            String newTitle = getTitleFromUrl(url);

            new AlertDialog.Builder(MainActivity.this).setTitle(newTitle).setMessage(message).setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    result.confirm();
                }
            }).setCancelable(false).create().show();
            return true;
            // return super.onJsAlert(view, url, message, result);
        }

        @Override
        public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {

            String newTitle = getTitleFromUrl(url);

            new AlertDialog.Builder(MainActivity.this).setTitle(newTitle).setMessage(message).setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    result.confirm();
                }
            }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    result.cancel();
                }
            }).setCancelable(false).create().show();
            return true;

            // return super.onJsConfirm(view, url, message, result);
        }

        // Android 2.x
        public void openFileChooser(ValueCallback<Uri> uploadMsg) {
            openFileChooser(uploadMsg, "");
        }

        // Android 3.0
        public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
            openFileChooser(uploadMsg, "", "filesystem");
        }

        // Android 4.1
        public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
            mUploadHandler = new UploadHandler(new Controller());
            mUploadHandler.openFileChooser(uploadMsg, acceptType, capture);
        }

        // Android 4.4, 4.4.1, 4.4.2
        // openFileChooser function is not called on Android 4.4, 4.4.1, 4.4.2,
        // you may use your own java script interface or other hybrid framework.          

        // Android 5.0.1
        public boolean onShowFileChooser(
                WebView webView, ValueCallback<Uri[]> filePathCallback,
                FileChooserParams fileChooserParams) {

            String acceptTypes[] = fileChooserParams.getAcceptTypes();

            String acceptType = "";
            for (int i = 0; i < acceptTypes.length; ++ i) {
                if (acceptTypes[i] != null && acceptTypes[i].length() != 0)
                    acceptType += acceptTypes[i] + ";";
            }
            if (acceptType.length() == 0)
                acceptType = "*/*";

            final ValueCallback<Uri[]> finalFilePathCallback = filePathCallback;

            ValueCallback<Uri> vc = new ValueCallback<Uri>() {

                @Override
                public void onReceiveValue(Uri value) {

                    Uri[] result;
                    if (value != null)
                        result = new Uri[]{value};
                    else
                        result = null;

                    finalFilePathCallback.onReceiveValue(result);

                }
            };

            openFileChooser(vc, acceptType, "filesystem");


            return true;
       }
    };

    class Controller {
        final static int FILE_SELECTED = 4;

        Activity getActivity() {
            return MainActivity.this;
        }
    }

    // copied from android-4.4.3_r1/src/com/android/browser/UploadHandler.java
    //////////////////////////////////////////////////////////////////////

    /*
     * Copyright (C) 2010 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */

    // package com.android.browser;
    //
    // import android.app.Activity;
    // import android.content.ActivityNotFoundException;
    // import android.content.Intent;
    // import android.net.Uri;
    // import android.os.Environment;
    // import android.provider.MediaStore;
    // import android.webkit.ValueCallback;
    // import android.widget.Toast;
    //
    // import java.io.File;
    // import java.util.Vector;
    //
    // /**
    // * Handle the file upload callbacks from WebView here
    // */
    // public class UploadHandler {

    class UploadHandler {
        /*
         * The Object used to inform the WebView of the file to upload.
         */
        private ValueCallback<Uri> mUploadMessage;
        private String mCameraFilePath;
        private boolean mHandled;
        private boolean mCaughtActivityNotFoundException;
        private Controller mController;
        public UploadHandler(Controller controller) {
            mController = controller;
        }
        String getFilePath() {
            return mCameraFilePath;
        }
        boolean handled() {
            return mHandled;
        }
        void onResult(int resultCode, Intent intent) {
            if (resultCode == Activity.RESULT_CANCELED && mCaughtActivityNotFoundException) {
                // Couldn't resolve an activity, we are going to try again so skip
                // this result.
                mCaughtActivityNotFoundException = false;
                return;
            }
            Uri result = intent == null || resultCode != Activity.RESULT_OK ? null
                    : intent.getData();
            // As we ask the camera to save the result of the user taking
            // a picture, the camera application does not return anything other
            // than RESULT_OK. So we need to check whether the file we expected
            // was written to disk in the in the case that we
            // did not get an intent returned but did get a RESULT_OK. If it was,
            // we assume that this result has came back from the camera.
            if (result == null && intent == null && resultCode == Activity.RESULT_OK) {
                File cameraFile = new File(mCameraFilePath);
                if (cameraFile.exists()) {
                    result = Uri.fromFile(cameraFile);
                    // Broadcast to the media scanner that we have a new photo
                    // so it will be added into the gallery for the user.
                    mController.getActivity().sendBroadcast(
                            new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
                }
            }
            mUploadMessage.onReceiveValue(result);
            mHandled = true;
            mCaughtActivityNotFoundException = false;
        }
        void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
            final String imageMimeType = "image/*";
            final String videoMimeType = "video/*";
            final String audioMimeType = "audio/*";
            final String mediaSourceKey = "capture";
            final String mediaSourceValueCamera = "camera";
            final String mediaSourceValueFileSystem = "filesystem";
            final String mediaSourceValueCamcorder = "camcorder";
            final String mediaSourceValueMicrophone = "microphone";
            // According to the spec, media source can be 'filesystem' or 'camera' or 'camcorder'
            // or 'microphone' and the default value should be 'filesystem'.
            String mediaSource = mediaSourceValueFileSystem;
            if (mUploadMessage != null) {
                // Already a file picker operation in progress.
                return;
            }
            mUploadMessage = uploadMsg;
            // Parse the accept type.
            String params[] = acceptType.split(";");
            String mimeType = params[0];
            if (capture.length() > 0) {
                mediaSource = capture;
            }
            if (capture.equals(mediaSourceValueFileSystem)) {
                // To maintain backwards compatibility with the previous implementation
                // of the media capture API, if the value of the 'capture' attribute is
                // "filesystem", we should examine the accept-type for a MIME type that
                // may specify a different capture value.
                for (String p : params) {
                    String[] keyValue = p.split("=");
                    if (keyValue.length == 2) {
                        // Process key=value parameters.
                        if (mediaSourceKey.equals(keyValue[0])) {
                            mediaSource = keyValue[1];
                        }
                    }
                }
            }
            //Ensure it is not still set from a previous upload.
            mCameraFilePath = null;
            if (mimeType.equals(imageMimeType)) {
                if (mediaSource.equals(mediaSourceValueCamera)) {
                    // Specified 'image/*' and requested the camera, so go ahead and launch the
                    // camera directly.
                    startActivity(createCameraIntent());
                    return;
                } else {
                    // Specified just 'image/*', capture=filesystem, or an invalid capture parameter.
                    // In all these cases we show a traditional picker filetered on accept type
                    // so launch an intent for both the Camera and image/* OPENABLE.
                    Intent chooser = createChooserIntent(createCameraIntent());
                    chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(imageMimeType));
                    startActivity(chooser);
                    return;
                }
            } else if (mimeType.equals(videoMimeType)) {
                if (mediaSource.equals(mediaSourceValueCamcorder)) {
                    // Specified 'video/*' and requested the camcorder, so go ahead and launch the
                    // camcorder directly.
                    startActivity(createCamcorderIntent());
                    return;
               } else {
                    // Specified just 'video/*', capture=filesystem or an invalid capture parameter.
                    // In all these cases we show an intent for the traditional file picker, filtered
                    // on accept type so launch an intent for both camcorder and video/* OPENABLE.
                    Intent chooser = createChooserIntent(createCamcorderIntent());
                    chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(videoMimeType));
                    startActivity(chooser);
                    return;
                }
            } else if (mimeType.equals(audioMimeType)) {
                if (mediaSource.equals(mediaSourceValueMicrophone)) {
                    // Specified 'audio/*' and requested microphone, so go ahead and launch the sound
                    // recorder.
                    startActivity(createSoundRecorderIntent());
                    return;
                } else {
                    // Specified just 'audio/*',  capture=filesystem of an invalid capture parameter.
                    // In all these cases so go ahead and launch an intent for both the sound
                    // recorder and audio/* OPENABLE.
                    Intent chooser = createChooserIntent(createSoundRecorderIntent());
                    chooser.putExtra(Intent.EXTRA_INTENT, createOpenableIntent(audioMimeType));
                    startActivity(chooser);
                    return;
                }
            }
            // No special handling based on the accept type was necessary, so trigger the default
            // file upload chooser.
            startActivity(createDefaultOpenableIntent());
        }
        private void startActivity(Intent intent) {
            try {
                mController.getActivity().startActivityForResult(intent, Controller.FILE_SELECTED);
            } catch (ActivityNotFoundException e) {
                // No installed app was able to handle the intent that
                // we sent, so fallback to the default file upload control.
                try {
                    mCaughtActivityNotFoundException = true;
                    mController.getActivity().startActivityForResult(createDefaultOpenableIntent(),
                            Controller.FILE_SELECTED);
                } catch (ActivityNotFoundException e2) {
                    // Nothing can return us a file, so file upload is effectively disabled.
                    Toast.makeText(mController.getActivity(), R.string.uploads_disabled,
                            Toast.LENGTH_LONG).show();
                }
            }
        }
        private Intent createDefaultOpenableIntent() {
            // Create and return a chooser with the default OPENABLE
            // actions including the camera, camcorder and sound
            // recorder where available.
            Intent i = new Intent(Intent.ACTION_GET_CONTENT);
            i.addCategory(Intent.CATEGORY_OPENABLE);
            i.setType("*/*");
            Intent chooser = createChooserIntent(createCameraIntent(), createCamcorderIntent(),
                    createSoundRecorderIntent());
            chooser.putExtra(Intent.EXTRA_INTENT, i);
            return chooser;
        }
        private Intent createChooserIntent(Intent... intents) {
            Intent chooser = new Intent(Intent.ACTION_CHOOSER);
            chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents);
            chooser.putExtra(Intent.EXTRA_TITLE,
                    mController.getActivity().getResources()
                            .getString(R.string.choose_upload));
            return chooser;
        }
        private Intent createOpenableIntent(String type) {
            Intent i = new Intent(Intent.ACTION_GET_CONTENT);
            i.addCategory(Intent.CATEGORY_OPENABLE);
            i.setType(type);
            return i;
        }
        private Intent createCameraIntent() {
            Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            File externalDataDir = Environment.getExternalStoragePublicDirectory(
                    Environment.DIRECTORY_DCIM);
            File cameraDataDir = new File(externalDataDir.getAbsolutePath() +
                    File.separator + "browser-photos");
            cameraDataDir.mkdirs();
            mCameraFilePath = cameraDataDir.getAbsolutePath() + File.separator +
                    System.currentTimeMillis() + ".jpg";
            cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mCameraFilePath)));
            return cameraIntent;
        }
        private Intent createCamcorderIntent() {
            return new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
        }
        private Intent createSoundRecorderIntent() {
            return new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
        }
    }
}

res/values/string.xml 的附加字符串资源:

<string name="uploads_disabled">File uploads are disabled.</string>
<string name="choose_upload">Choose file for upload</string>

如果你使用 proguard,你可能需要 proguard-project.txt 中的以下选项:

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

更新 #1 (2015.09.09)

为 Android 5.0.1 的兼容性添加代码。

【讨论】:

  • 无法让它在 4.4.2 中工作。你在android 4.4.2上测试过吗
  • @Anna openFileChooser 函数未在 Android 4.4、4.4.1、4.4.2 上调用。此错误已在 Android 4.4.3 上修复。参考)http://code.google.com/p/android/issues/detail?id=62220,但遗憾的是,很多人使用 4.4.2。我认为您可以使用自己的 java 脚本接口(参见 DanelK 的答案)或其他混合框架(cordova、phonegap、agate ...)。就我而言,我无法修改服务器上的 HTML 文件。所以我使用了 Agate WebView Java Script Plugin。 AgateWebViewFileUpload_AndroidEclipse
  • @UnknownStack 我也在尝试实现玛瑙 Webview Fil 上传。我想为 webview 设置一个 url 而不是 htm 页面。我该怎么做?
  • @tusharnarang 打开“assets/moml/ui/webView.xml”文件。然后,找到 AGATEWEBVIEW.src 属性并将“index.htm”值替换为您的 URL。 (参考github.com/applusform/WebViewFileUploadFix
  • @UnknownStack。如果我按下后退按钮,应用程序将关闭。在您的代码中在哪里使用这段代码..public boolean onKeyDown(int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_BACK) && this.webView.canGoBack( )) { this.webView.goBack();返回真; } 返回 super.onKeyDown(keyCode, event); }
【解决方案8】:

适用于 Android 8 的 Kotlin 解决方案:

private var mUploadMessage: ValueCallback<Uri>? = null
private var uploadMessage: ValueCallback<Array<Uri>>? = null

常量:

const val FILECHOOSER_RESULTCODE = 1
const val REQUEST_SELECT_FILE = 100

WebView 设置:

webView.webChromeClient = object : WebChromeClient() {
        override fun onPermissionRequest(request: PermissionRequest?) {
            Log.d("MainActivity", "onPermissionRequest")
            requestPermission(request)
        }


        // For Android 3.0+
        fun openFileChooser(uploadMsg: ValueCallback<*>, acceptType: String) {
            mUploadMessage = uploadMsg as ValueCallback<Uri>
            val i = Intent(Intent.ACTION_GET_CONTENT)
            i.addCategory(Intent.CATEGORY_OPENABLE)
            i.type = "*/*"
            this@MainActivity.startActivityForResult(
                    Intent.createChooser(i, "File Browser"),
                    FILECHOOSER_RESULTCODE)
        }

        //For Android 4.1
        fun openFileChooser(uploadMsg: ValueCallback<Uri>, acceptType: String, capture: String) {
            mUploadMessage = uploadMsg
            val i = Intent(Intent.ACTION_GET_CONTENT)
            i.addCategory(Intent.CATEGORY_OPENABLE)
            i.type = "image/*"
            this@MainActivity.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE)

        }

        protected fun openFileChooser(uploadMsg: ValueCallback<Uri>) {
            mUploadMessage = uploadMsg
            val intent = Intent(Intent.ACTION_GET_CONTENT)
            intent.addCategory(Intent.CATEGORY_OPENABLE)
            intent.type = "*/*"
            startActivityForResult(Intent.createChooser(intent, "File Chooser"), FILECHOOSER_RESULTCODE)
        }

        override fun onShowFileChooser(webView: WebView?, filePathCallback: ValueCallback<Array<Uri>>?, fileChooserParams: FileChooserParams?): Boolean {
            uploadMessage?.onReceiveValue(null)
            uploadMessage = null

            uploadMessage = filePathCallback

            val intent = fileChooserParams!!.createIntent()
            try {
                startActivityForResult(intent, REQUEST_SELECT_FILE)
            } catch (e: ActivityNotFoundException) {
                uploadMessage = null
                Toast.makeText(applicationContext, "Cannot Open File Chooser", Toast.LENGTH_LONG).show()
                return false
            }

            return true
        }

    }

还有 onAcrtivityResult 部分:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        if (requestCode === REQUEST_SELECT_FILE) {
            if (uploadMessage == null)
                return
            print("result code = " + resultCode)
            var results: Array<Uri>? = WebChromeClient.FileChooserParams.parseResult(resultCode, data)
            uploadMessage?.onReceiveValue(results)
            uploadMessage = null
        }
    } else if (requestCode === FILECHOOSER_RESULTCODE) {
        if (null == mUploadMessage)
            return
        // Use MainActivity.RESULT_OK if you're implementing WebView inside Fragment
        // Use RESULT_OK only if you're implementing WebView inside an Activity
        val result = if (intent == null || resultCode !== RESULT_OK) null else intent.data
        mUploadMessage?.onReceiveValue(result)
        mUploadMessage = null
    } else
        Toast.makeText(applicationContext, "Failed to Upload Image", Toast.LENGTH_LONG).show()
}

请注意我们的意图变量称为“数据”。

【讨论】:

  • 谢谢!我请你喝啤酒
  • 谢谢!也适用于 Android 10 :)
  • 您是否有一个使用选择器意图的版本,以便您可以通过文件浏览器或相机上传图像?如果你愿意,请分享
  • 很棒的答案!!
  • 应该是 2021 年公认的答案:) 也适用于 Android 11 :)
【解决方案9】:

这对我有用。也适用于牛轧糖和棉花糖[[

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private final static int FCR = 1;
    WebView webView;
    private String mCM;
    private ValueCallback<Uri> mUM;
    private ValueCallback<Uri[]> mUMA;

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);

        if (Build.VERSION.SDK_INT >= 21) {
            Uri[] results = null;

            //Check if response is positive
            if (resultCode == Activity.RESULT_OK) {
                if (requestCode == FCR) {

                    if (null == mUMA) {
                        return;
                    }
                    if (intent == null) {
                        //Capture Photo if no image available
                        if (mCM != null) {
                            results = new Uri[]{Uri.parse(mCM)};
                        }
                    } else {
                        String dataString = intent.getDataString();
                        if (dataString != null) {
                            results = new Uri[]{Uri.parse(dataString)};
                        }
                    }
                }
            }
            mUMA.onReceiveValue(results);
            mUMA = null;
        } else {

            if (requestCode == FCR) {
                if (null == mUM) return;
                Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
                mUM.onReceiveValue(result);
                mUM = null;
            }
        }
    }

    @SuppressLint({"SetJavaScriptEnabled", "WrongViewCast"})
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (Build.VERSION.SDK_INT >= 23 && (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, 1);
        }

        webView = (WebView) findViewById(R.id.ifView);
        assert webView != null;

        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setAllowFileAccess(true);

        if (Build.VERSION.SDK_INT >= 21) {
            webSettings.setMixedContentMode(0);
            webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
        } else if (Build.VERSION.SDK_INT >= 19) {
            webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
        } else if (Build.VERSION.SDK_INT < 19) {
            webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }

        webView.setWebViewClient(new Callback());
        webView.loadUrl("https://infeeds.com/");
        webView.setWebChromeClient(new WebChromeClient() {

            //For Android 3.0+
            public void openFileChooser(ValueCallback<Uri> uploadMsg) {

                mUM = uploadMsg;
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("*/*");
                MainActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), FCR);
            }

            // For Android 3.0+, above method not supported in some android 3+ versions, in such case we use this
            public void openFileChooser(ValueCallback uploadMsg, String acceptType) {

                mUM = uploadMsg;
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("*/*");
                MainActivity.this.startActivityForResult(
                        Intent.createChooser(i, "File Browser"),
                        FCR);
            }

            //For Android 4.1+
            public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {

                mUM = uploadMsg;
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("*/*");
                MainActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), MainActivity.FCR);
            }

            //For Android 5.0+
            public boolean onShowFileChooser(
                    WebView webView, ValueCallback<Uri[]> filePathCallback,
                    WebChromeClient.FileChooserParams fileChooserParams) {

                if (mUMA != null) {
                    mUMA.onReceiveValue(null);
                }

                mUMA = filePathCallback;
                Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                if (takePictureIntent.resolveActivity(MainActivity.this.getPackageManager()) != null) {

                    File photoFile = null;

                    try {
                        photoFile = createImageFile();
                        takePictureIntent.putExtra("PhotoPath", mCM);
                    } catch (IOException ex) {
                        Log.e(TAG, "Image file creation failed", ex);
                    }
                    if (photoFile != null) {
                        mCM = "file:" + photoFile.getAbsolutePath();
                        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
                    } else {
                        takePictureIntent = null;
                    }
                }

                Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
                contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
                contentSelectionIntent.setType("*/*");
                Intent[] intentArray;

                if (takePictureIntent != null) {
                    intentArray = new Intent[]{takePictureIntent};
                } else {
                    intentArray = new Intent[0];
                }

                Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
                chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
                chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
                chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
                startActivityForResult(chooserIntent, FCR);

                return true;
            }
        });
    }

    // Create an image file
    private File createImageFile() throws IOException {

        @SuppressLint("SimpleDateFormat") String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = "img_" + timeStamp + "_";
        File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        return File.createTempFile(imageFileName, ".jpg", storageDir);
    }

    @Override
    public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {

        if (event.getAction() == KeyEvent.ACTION_DOWN) {

            switch (keyCode) {
                case KeyEvent.KEYCODE_BACK:

                    if (webView.canGoBack()) {
                        webView.goBack();
                    } else {
                        finish();
                    }

                    return true;
            }
        }

        return super.onKeyDown(keyCode, event);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
    }

    public class Callback extends WebViewClient {
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            Toast.makeText(getApplicationContext(), "Failed loading app!", Toast.LENGTH_SHORT).show();
        }
    }
}

【讨论】:

    【解决方案10】:

    我发现有必要在 Android 4.1 中定义 public void openFileChooser(ValueCallback&lt;Uri&gt; uploadMsg, String acceptType, String capture)。然后我遵循了 Michel Olivier 的解决方案。

    【讨论】:

    • 你能澄清一下我应该在哪里定义这个方法吗?以及我应该如何使用它?
    【解决方案11】:

    我实际上设法让文件选择器出现在 Kitkat 中,选择图像并在活动结果中获取文件路径,但我唯一无法“修复”(导致此解决方法)的事情是进行输入归档以填写文件数据。

    有谁知道如何从活动中访问输入字段?我正在使用这个例子comment。只是这最后一块,墙上的最后一块砖,我只需要放在正确的位置(我可以直接从代码触发图像文件的上传。

    更新 #1

    我不是铁杆 Android 开发人员,所以我将在新手级别显示代码。我在现有的 Activity 中创建一个新的 Activity

    清单部分

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <application android:label="TestApp">
     <activity android:name=".BrowseActivity"></activity>
    </application>
    

    我正在从这个示例 answer 创建我的 BrowseActivity 类。 WebChromeClient() 实例看起来基本一样,除了最后一块,触发了选择器 UI 部分...

    private final static int FILECHOOSER_RESULTCODE=1;  
    private final static int KITKAT_RESULTCODE = 2;
    
    ...
    
    // The new WebChromeClient() looks pretty much the same, except one piece...
    
    WebChromeClient chromeClient = new WebChromeClient(){  
        // For Android 3.0+
        public void openFileChooser(ValueCallback<Uri> uploadMsg) { /* Default code */ }  
    
        // For Android 3.0+
        public void openFileChooser( ValueCallback uploadMsg, String acceptType ) { /* Default code */ }  
    
        //For Android 4.1, also default but it'll be as example
        public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
            mUploadMessage = uploadMsg;
            Intent i = new Intent(Intent.ACTION_GET_CONTENT);
            i.addCategory(Intent.CATEGORY_OPENABLE);
            i.setType("*/*");
            BrowseActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), BrowseActivity.FILECHOOSER_RESULTCODE);
    
        }  
    
        // The new code
        public void showPicker( ValueCallback<Uri> uploadMsg ){  
            // Here is part of the issue, the uploadMsg is null since it is not triggered from Android
            mUploadMessage = uploadMsg; 
            Intent i = new Intent(Intent.ACTION_GET_CONTENT);
            i.addCategory(Intent.CATEGORY_OPENABLE);
            i.setType("*/*");
            BrowseActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), BrowseActivity.KITKAT_RESULTCODE);
        }}
    

    还有一些东西

    web = new WebView(this);
    // Notice this part, setting chromeClient as js interface is just lazy
    web.getSettings().setJavaScriptEnabled(true);
    web.addJavascriptInterface(chromeClient, "jsi" );
    web.getSettings().setAllowFileAccess(true);
    web.getSettings().setAllowContentAccess(true);
    web.clearCache(true);
    web.loadUrl( "http://as3breeze.com/upload.html" );
    web.setWebViewClient(new myWebClient());
    web.setWebChromeClient(chromeClient);
    
    
    @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) {  
      Log.d("Result", "("+requestCode+ ") - (" +resultCode  + ") - (" + intent + ") - " + mUploadMessage);  
        if (null == intent) return;  
        Uri result = null;  
        if(requestCode==FILECHOOSER_RESULTCODE)  
        {  
            Log.d("Result","Old android");  
            if (null == mUploadMessage) return;  
            result = intent == null || resultCode != RESULT_OK ? null  : intent.getData();  
            mUploadMessage.onReceiveValue(result);  
            mUploadMessage = null;  
        } else if (requestCode == KITKAT_RESULTCODE) {  
            Log.d("Result","Kitkat android");  
            result = intent.getData();  
            final int takeFlags = intent.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION  | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);  
            String path = getPath( this, result);  
            File selectedFile = new File(path); 
    //I used you example with a bit of editing so thought i would share, here i added a method to upload the file to the webserver
    File selectedFile = new File(path);  
                UploadFile(selectedFile);
    
    
            //mUploadMessage.onReceiveValue( Uri.parse(selectedFile.toString()) );  
            // Now we have the file but since mUploadMessage was null, it gets errors
        }  
    }
    
     public void UploadFile(File selectedFile)
    {
        Random rnd = new Random();
        String sName = "File" + rnd.nextInt(999999) + selectedFile.getAbsolutePath().substring(selectedFile.getAbsolutePath().lastIndexOf("."));
        UploadedFileName = sName;
        uploadFile = selectedFile;
        if (progressBar != null && progressBar.isShowing())
        {
            progressBar.dismiss();
        }
     // prepare for a progress bar dialog
        progressBar = new ProgressDialog(mContext);
        progressBar.setCancelable(true);
        progressBar.setMessage("Uploading File");
        progressBar.setProgressStyle(ProgressDialog.STYLE_SPINNER);            
        progressBar.show();
        new Thread() {
    
            public void run() 
            {
                int serverResponseCode;
                String serverResponseMessage;
                HttpURLConnection connection = null;
                DataOutputStream outputStream = null;
                DataInputStream inputStream = null;
                String pathToOurFile = uploadFile.getAbsolutePath();
                String urlServer = "http://serveraddress/Scripts/UploadHandler.php?name" + UploadedFileName;
                String lineEnd = "\r\n";
                String twoHyphens = "--";
                String boundary =  "*****";
    
                int bytesRead, bytesAvailable, bufferSize;
                byte[] buffer;
                int maxBufferSize = 1*1024*1024;
    
                try
                {
                    FileInputStream fileInputStream = new FileInputStream(uploadFile);
    
                    URL url = new URL(urlServer);
                    connection = (HttpURLConnection) url.openConnection();
                    Log.i("File", urlServer);
    
                    // Allow Inputs &amp; Outputs.
                    connection.setDoInput(true);
                    connection.setDoOutput(true);
                    connection.setUseCaches(false);
    
                    // Set HTTP method to POST.
                    connection.setRequestMethod("POST");
    
                    connection.setRequestProperty("Connection", "Keep-Alive");
                    connection.setRequestProperty("Content-Type", "multipart/form-data;boundary="+boundary);
                    Log.i("File", "Open conn");
    
                    outputStream = new DataOutputStream( connection.getOutputStream() );
    
                    outputStream.writeBytes(twoHyphens + boundary + lineEnd);
                    outputStream.writeBytes("Content-Disposition: form-data; name=\"uploadedfile\";filename=\"" + pathToOurFile +"\"" + lineEnd);
                    outputStream.writeBytes(lineEnd);
                    Log.i("File", "write bytes");
    
                    bytesAvailable = fileInputStream.available();
                    bufferSize = Math.min(bytesAvailable, maxBufferSize);
                    buffer = new byte[bufferSize];
                    Log.i("File", "available: " + fileInputStream.available());
    
                    // Read file
                    bytesRead = fileInputStream.read(buffer, 0, bufferSize);
    
                    Log.i("file", "Bytes Read: " + bytesRead);
                    while (bytesRead > 0)
                    {
                        outputStream.write(buffer, 0, bufferSize);
                        bytesAvailable = fileInputStream.available();
                        bufferSize = Math.min(bytesAvailable, maxBufferSize);
                        bytesRead = fileInputStream.read(buffer, 0, bufferSize);
                    }
    
                    outputStream.writeBytes(lineEnd);
                    outputStream.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
    
                    // Responses from the server (code and message)
                    serverResponseCode = connection.getResponseCode();
                    serverResponseMessage = connection.getResponseMessage();
                    Log.i("file repsonse", serverResponseMessage);
    
    //once the file is uploaded call a javascript function to verify the user wants to save the image
                    progressBar.dismiss();
                    runOnUiThread(new Runnable() 
                    {
    
                        @Override
                        public void run() 
                        {
                            Log.i("start", "File name: " + UploadedFileName);
                            WebView myWebView = (WebView) findViewById(R.id.webview);
                            myWebView.loadUrl("javascript:CheckImage('" + UploadedFileName + "')");
                        }
                    });
    
    
                    fileInputStream.close();
                    outputStream.flush();
                    outputStream.close();
                }
                catch (Exception ex)
                {
                    Log.i("exception", "Error: " + ex.toString());
                }               
            }
        }.start();
    

    }

    最后,一些更多的代码来获取实际的文件路径,在 SO 上找到的代码,我也在 cmets 中添加了 post url,因此作者的工作获得了学分。

    /**
     * Get a file path from a Uri. This will get the the path for Storage Access
     * Framework Documents, as well as the _data field for the MediaStore and
     * other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @author paulburke
     * @source https://stackoverflow.com/a/20559175
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static String getPath(final Context context, final Uri uri) {
    
        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
    
        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
    
                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }
    
                // TODO handle non-primary volumes
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {
    
                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
    
                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
    
                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }
    
                final String selection = "_id=?";
                final String[] selectionArgs = new String[] {
                        split[1]
                };
    
                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }
    
        return null;
    }
    
    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @param selection (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     * @source https://stackoverflow.com/a/20559175
     */
    public static String getDataColumn(Context context, Uri uri, String selection,
                                       String[] selectionArgs) {
    
        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {
                column
        };
    
        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }
    
    
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     * @source https://stackoverflow.com/a/20559175
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }
    
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     * @source https://stackoverflow.com/a/20559175
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }
    
    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     * @source https://stackoverflow.com/a/20559175
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }  
    

    最后,HTML 页面需要触发 showPicker 的新方法(特别是在 A4.4 上时)

    <form id="form-upload" method="post" enctype="multipart/form-data">
        <input id="fileupload" name="fileupload" type="file" onclick="javascript:prepareForPicker();"/>
    </form>
    <script type="text/javascript">
    function getAndroidVersion() {
        var ua = navigator.userAgent; 
        var match = ua.match(/Android\s([0-9\.]*)/);
        return match ? match[1] : false;
    };
    function prepareForPicker(){
        if(getAndroidVersion().indexOf("4.4") != -1){
            window.jsi.showPicker();
            return false;
        }
    }
    
    function CheckImage(name)
    {
    //Check to see if user wants to save I used some ajax to save the file if necesarry
    }
    </script>
    

    【讨论】:

    • 为迟到的答案道歉,这个没有任何工作演示。这在当时是一项简单的研究。对不起:(
    【解决方案12】:

    2019:此代码对我有用(在 Android 5 - 9 上测试)。

    package com.example.filechooser;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.net.Uri;
    import android.net.http.SslError;
    import android.os.Bundle;
    import android.webkit.SslErrorHandler;
    import android.webkit.ValueCallback;
    import android.webkit.WebChromeClient;
    import android.webkit.WebView;
    import android.webkit.WebViewClient;
    
    
    public class MainActivity extends Activity {
    
        // variables para manejar la subida de archivos
        private final static int FILECHOOSER_RESULTCODE = 1;
        private ValueCallback<Uri[]> mUploadMessage;
    
        // variable para manejar el navegador empotrado
        WebView mainWebView;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
    
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_main);
    
            // instanciamos el webview
            mainWebView = findViewById(R.id.main_web_view);
    
            // establecemos el cliente interno para que la navegacion no se salga de la aplicacion
            mainWebView.setWebViewClient(new MyWebViewClient());
    
            // establecemos el cliente chrome para seleccionar archivos
            mainWebView.setWebChromeClient(new MyWebChromeClient());
    
            // configuracion del webview
            mainWebView.getSettings().setJavaScriptEnabled(true);
    
            // cargamos la pagina
            mainWebView.loadUrl("https://example.com");
        }
    
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    
            // manejo de seleccion de archivo
            if (requestCode == FILECHOOSER_RESULTCODE) {
    
                if (null == mUploadMessage || intent == null || resultCode != RESULT_OK) {
                    return;
                }
    
                Uri[] result = null;
                String dataString = intent.getDataString();
    
                if (dataString != null) {
                    result = new Uri[]{ Uri.parse(dataString) };
                }
    
                mUploadMessage.onReceiveValue(result);
                mUploadMessage = null;
            }
        }
    
    
        // ====================
        // Web clients classes
        // ====================
    
        /**
         * Clase para configurar el webview
         */
        private class MyWebViewClient extends WebViewClient {
    
            // permite la navegacion dentro del webview
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        }
    
    
        /**
         * Clase para configurar el chrome client para que nos permita seleccionar archivos
         */
        private class MyWebChromeClient extends WebChromeClient {
    
            // maneja la accion de seleccionar archivos
            @Override
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
    
                // asegurar que no existan callbacks
                if (mUploadMessage != null) {
                    mUploadMessage.onReceiveValue(null);
                }
    
                mUploadMessage = filePathCallback;
    
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("*/*"); // set MIME type to filter
    
                MainActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), MainActivity.FILECHOOSER_RESULTCODE );
    
                return true;
            }
        }
    
    }
    

    希望能帮到你。

    【讨论】:

    • 对于 onActivityResult 方法它说调用超级缺失
    • @sumitkanoje 尝试将调用 super 放在 if 块之前: super.onActivityResult(requestCode, resultCode, intent);
    【解决方案13】:

    Google 自己的浏览器为这个问题提供了如此全面的解决方案,它保证了它自己的类:

    openFileChooser implementation in Android 4.0.4

    UploadHandler class in Android 4.0.4

    【讨论】:

      【解决方案14】:

      找到了一个适合我的解决方案!在文件proguard-android.txt中再添加一条规则:

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

      【讨论】:

      • 您使用的是哪个版本的 Android?
      • 4.4.4 CyanogemMode 11
      【解决方案15】:

      【讨论】:

      • 感谢您的建议。实际问题是 - webview 没有向我显示文件上传对话框,我可以在其中选择要上传的文件。所以,首先我需要一个能够显示文件上传对话框的解决方案,然后我可以参考你建议的链接。
      • 感谢您的 4 条建议 Pragna。 None works... 实际上我观察到的一件事是,覆盖 WebChromeClient 的 openFileChooser(ValueCallback) 方法在 android 2.2 中有效,其中单击 中的字段UI,便于从图库中选择文件,但是,在 android 3.0 中同样不起作用...there4,我想尝试在 webview 中使用 flash 文件上传来上传文件,但在 android 3.0 模拟器中,默认情况下没有安装 flash 并且安装它市场应用程序不可用等等并发症......最后,解决方案似乎不可能与给定!
      【解决方案16】:

      Webview - 单个和多个文件选择

      你需要两分钟来实现这段代码:

      build.gradle

      implementation 'com.github.angads25:filepicker:1.1.1'
      

      java代码:

      import android.annotation.SuppressLint;
      import android.app.Activity;
      import android.content.DialogInterface;
      import android.content.Intent;
      import android.content.pm.PackageManager;
      import android.graphics.Bitmap;
      import android.net.Uri;
      import android.os.Build;
      import android.os.Bundle;
      import android.support.annotation.NonNull;
      import android.util.Log;
      import android.view.KeyEvent;
      import android.view.View;
      import android.webkit.ValueCallback;
      import android.webkit.WebChromeClient;
      import android.webkit.WebSettings;
      import android.webkit.WebView;
      import android.webkit.WebViewClient;
      import android.widget.ProgressBar;
      import android.widget.Toast;
      
      import com.bivoiclient.utils.Constants;
      import com.github.angads25.filepicker.controller.DialogSelectionListener;
      import com.github.angads25.filepicker.model.DialogConfigs;
      import com.github.angads25.filepicker.model.DialogProperties;
      import com.github.angads25.filepicker.view.FilePickerDialog;
      
      import java.io.File;
      
      public class WebBrowserScreen extends Activity {
      
          private WebView webView;
          private ValueCallback<Uri[]> mUploadMessage;
          private FilePickerDialog dialog;
          private String LOG_TAG = "DREG";
          private Uri[] results;
      
          @SuppressLint("SetJavaScriptEnabled")
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_complain);
      
              webView = findViewById(R.id.webview);
              WebSettings webSettings = webView.getSettings();
              webSettings.setAppCacheEnabled(true);
              webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
              webSettings.setJavaScriptEnabled(true);
              webSettings.setLoadWithOverviewMode(true);
              webSettings.setAllowFileAccess(true);
              webView.setWebViewClient(new PQClient());
              webView.setWebChromeClient(new PQChromeClient());
              if (Build.VERSION.SDK_INT >= 19) {
                  webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
              } else {
                  webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
              }
      
              webView.loadUrl(Constants.COMPLAIN_URL);
      
          }
      
          private void openFileSelectionDialog() {
      
              if (null != dialog && dialog.isShowing()) {
                  dialog.dismiss();
              }
      
              //Create a DialogProperties object.
              final DialogProperties properties = new DialogProperties();
      
              //Instantiate FilePickerDialog with Context and DialogProperties.
              dialog = new FilePickerDialog(WebBrowserScreen.this, properties);
              dialog.setTitle("Select a File");
              dialog.setPositiveBtnName("Select");
              dialog.setNegativeBtnName("Cancel");
              properties.selection_mode = DialogConfigs.MULTI_MODE; // for multiple files
      //        properties.selection_mode = DialogConfigs.SINGLE_MODE; // for single file
              properties.selection_type = DialogConfigs.FILE_SELECT;
      
              //Method handle selected files.
              dialog.setDialogSelectionListener(new DialogSelectionListener() {
                  @Override
                  public void onSelectedFilePaths(String[] files) {
                      results = new Uri[files.length];
                      for (int i = 0; i < files.length; i++) {
                          String filePath = new File(files[i]).getAbsolutePath();
                          if (!filePath.startsWith("file://")) {
                              filePath = "file://" + filePath;
                          }
                          results[i] = Uri.parse(filePath);
                          Log.d(LOG_TAG, "file path: " + filePath);
                          Log.d(LOG_TAG, "file uri: " + String.valueOf(results[i]));
                      }
                      mUploadMessage.onReceiveValue(results);
                      mUploadMessage = null;
                  }
              });
              dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
                  @Override
                  public void onCancel(DialogInterface dialogInterface) {
                      if (null != mUploadMessage) {
                          if (null != results && results.length >= 1) {
                              mUploadMessage.onReceiveValue(results);
                          } else {
                              mUploadMessage.onReceiveValue(null);
                          }
                      }
                      mUploadMessage = null;
                  }
              });
              dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
                  @Override
                  public void onDismiss(DialogInterface dialogInterface) {
                      if (null != mUploadMessage) {
                          if (null != results && results.length >= 1) {
                              mUploadMessage.onReceiveValue(results);
                          } else {
                              mUploadMessage.onReceiveValue(null);
                          }
                      }
                      mUploadMessage = null;
                  }
              });
      
              dialog.show();
      
          }
      
          public class PQChromeClient extends WebChromeClient {
      
              @Override
              public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
                  // Double check that we don't have any existing callbacks
                  if (mUploadMessage != null) {
                      mUploadMessage.onReceiveValue(null);
                  }
                  mUploadMessage = filePathCallback;
      
                  openFileSelectionDialog();
      
                  return true;
              }
      
          }
      
          //Add this method to show Dialog when the required permission has been granted to the app.
          @Override
          public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
              switch (requestCode) {
                  case FilePickerDialog.EXTERNAL_READ_PERMISSION_GRANT: {
                      if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                          if (dialog != null) {
                              openFileSelectionDialog();
                          }
                      } else {
                          //Permission has not been granted. Notify the user.
                          Toast.makeText(WebBrowserScreen.this, "Permission is Required for getting list of files", Toast.LENGTH_SHORT).show();
                      }
                  }
              }
          }
      
          public boolean onKeyDown(int keyCode, KeyEvent event) {
              // Check if the key event was the Back button and if there's history
              if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
                  webView.goBack();
                  return true;
              }
              // If it wasn't the Back key or there's no web page history, bubble up to the default
              // system behavior (probably exit the activity)
      
              return super.onKeyDown(keyCode, event);
          }
      
      
          public class PQClient extends WebViewClient {
              ProgressBar progressDialog;
      
              public boolean shouldOverrideUrlLoading(WebView view, String url) {
      
                  // If url contains mailto link then open Mail Intent
                  if (url.contains("mailto:")) {
      
                      // Could be cleverer and use a regex
                      //Open links in new browser
                      view.getContext().startActivity(
                              new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
      
                      // Here we can open new activity
      
                      return true;
      
                  } else {
                      // Stay within this webview and load url
                      view.loadUrl(url);
                      return true;
                  }
              }
      
              // Show loader on url load
              public void onPageStarted(WebView view, String url, Bitmap favicon) {
                  // Then show progress  Dialog
                  // in standard case YourActivity.this
                  if (progressDialog == null) {
                      progressDialog = findViewById(R.id.progressBar);
                      progressDialog.setVisibility(View.VISIBLE);
                  }
              }
      
              // Called when all page resources loaded
              public void onPageFinished(WebView view, String url) {
                  webView.loadUrl("javascript:(function(){ " +
                          "document.getElementById('android-app').style.display='none';})()");
      
                  try {
                      // Close progressDialog
                      progressDialog.setVisibility(View.GONE);
                  } catch (Exception exception) {
                      exception.printStackTrace();
                  }
              }
          }
      
      }
      

      【讨论】:

      • 这一行显示错误 import com.bivoiclient.utils.Constants;
      • 删除此导入并将您的网址设置为此:webView.loadUrl("google.com");
      • 嗨,Ahamadullah Saikat,为了付出这样的努力,它就像自定义一样工作......默认情况下不工作。如果任何用户想要在 webview 中默认打开附件选项,他/她将如何做到这一点。它是一种解决方法。
      【解决方案17】:

      在 KitKat 中,您可以使用存储访问框架。

      Storage Access Framework / Writing a Client App

      【讨论】:

      • “也许”?您能为您的帖子提供更多背景信息吗?请阅读How to Answer
      • 是否有示例代码可以在android 4.4.2 中使用webview 实现它?
      【解决方案18】:

      科特林 07.10.2021 |高级VebView |活动/片段


      解决方案: https://github.com/delight-im/Android-AdvancedWebView


      Fragent 解决方案:

      activityfragment的区别只在于onActivityResult:

      片段:

      lateinit var webViewGlobal: AdvancedWebView private set
      
      class WebViewFragment : Fragment(), AdvancedWebView.Listener {
      
      private lateinit var binding: FragmentWebViewBinding
      
      override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
          binding = FragmentWebViewBinding.inflate(inflater)
          webViewGlobal = binding.webWiew
          return binding.root
          }
      }
      

      活动:

      class MainActivity : AppCompatActivity() {
          override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
              webViewGlobal.onActivityResult(requestCode, resultCode, data)
              super.onActivityResult(requestCode, resultCode, data)
          }
      }
      

      Android 11:

      <application
             ...
             android:requestLegacyExternalStorage="true"
             ...
      />
      

      清单:

       <uses-permission android:name="android.permission.CAMERA/>
       <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE/>
      

      PS。 Vel_daN:爱​​你所做的事情?。

      【讨论】:

        【解决方案19】:

        我是 Andriod 的新手,也为此苦苦挣扎。根据 Google 参考指南 WebView。

        默认情况下,WebView 不提供类似浏览器的小部件,不提供 启用 JavaScript 和网页错误将被忽略。如果你的目标是 只是为了显示一些 HTML 作为 UI 的一部分,这可能很好; 用户除了阅读网页之外不需要与网页进行交互, 并且网页不需要与用户交互。如果你真的 想要一个成熟的网络浏览器,那么你可能想要调用 带有 URL Intent 的浏览器应用程序,而不是使用 网络视图。

        我在 MainActvity.java 中执行的示例代码。

         Uri uri = Uri.parse("https://www.example.com");
         Intent intent = new Intent(Intent.ACTION_VIEW, uri);
         startActivity(intent);
        

        执行

        package example.com.myapp;
        
        import android.support.v7.app.AppCompatActivity;
        import android.os.Bundle;
        import android.webkit.WebView;
        import android.webkit.WebViewClient;
        import android.content.Intent;
        import android.net.Uri;
        
        public class MainActivity extends AppCompatActivity {
        
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
        
                Uri uri = Uri.parse("http://www.example.com/");
                Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                startActivity(intent);
                getSupportActionBar().hide();
            }}
        

        【讨论】:

          【解决方案20】:

          我在这个问题上研究了将近一个月。一切都失败了。在多个网站上看到的所有代码都不好用。但是这里有最好的解决方案

          https://github.com/chiclaim/android-webview-upload-file

          步骤

          1) 点击克隆或下载

          2) 获取本地目录中的 zip 文件

          3) 解压压缩文件

          4) 打开安卓工作室

          5) 转到文件 ----> 打开 ---> 导航到您解压缩内容的目录。

          6) 在 webView.loadUrl("your url hre"); 中更改所需的网址在 MainActivity.java

          7) 适用于 Android Studio 3.4.2 版本

          【讨论】:

            【解决方案21】:

            自定义 WebChromeClient:

            class AppChromeClient(private val fragmentWeakReference: WeakReference<WebViewFragment>) :
            WebChromeClient() {
            private var openFileCallback: ValueCallback<Array<Uri>>? = null
            
            override fun onShowFileChooser(
                webView: WebView?,
                filePathCallback: ValueCallback<Array<Uri>>?,
                fileChooserParams: FileChooserParams?
            ): Boolean {
                if (filePathCallback == null) {
                    return (super.onShowFileChooser(webView, filePathCallback, fileChooserParams))
                }
                openFileCallback = filePathCallback
                val webViewFragment = fragmentWeakReference.get() ?: return false
                webViewFragment.launchGetMultipleContents("*/*")
            
                return true
            }
            
            fun receiveFileCallback(result: Array<Uri>) {
                openFileCallback?.onReceiveValue(result)
                openFileCallback = null
            }
            }
            

            WebViewFragment:

            class WebViewFragment : Fragment() {
                private var _binding: FragmentWebviewBinding? = null
                private val binding get() = _binding!!
                private lateinit var webView: WebView
                private val chromeClient = AppChromeClient(WeakReference(this))
                private var contentLauncher: ActivityResultLauncher<String> = getMultipleContentLauncher()
            
                override fun onCreateView(
                    inflater: LayoutInflater,
                    container: ViewGroup?,
                    savedInstanceState: Bundle?
                ): View {
                    _binding = FragmentWebviewBinding.inflate(inflater, container, false)
            
                    webView.webChromeClient = chromeClient
                    val url = requireContext().getString(R.string.app_domain)
                    webView.setting.javaScriptEnabled = true
                    webView.loadUrl(url)
                    return binding.root
                }
            
            
                private fun getMultipleContentLauncher(): ActivityResultLauncher<String> {
                    return this.registerForActivityResult(ActivityResultContracts.GetMultipleContents()) { list ->
                        if (list.isEmpty()) {
                            showToast("No files selected")
                        }
                        chromeClient.receiveFileCallback(list.toTypedArray())
                    }
                }
            
            
                fun launchGetMultipleContents(type: String) {
                    contentLauncher.launch(type)
                }
            }
            

            【讨论】:

              猜你喜欢
              • 2012-07-28
              • 2018-01-18
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-08-02
              • 2012-06-12
              • 1970-01-01
              相关资源
              最近更新 更多