从Webview输入字段上传相机照片和文件选择器

7

我的应用是基于Web的,我需要从一个INPUT字段中上传图片。 我有两种情况,由于我不知道其他方法,因此根据页面选择"boolean boolFileChoser"之一取决于其URL请求:

a. 文件选择器

b. 相机拍摄照片。

我已经处理了文件选择器并且它可以完美地上传文件,问题在于相机。当我尝试上传相机照片时,它崩溃了。 据我所知,这是由于URI造成的。

a)文件选择器:content://media/external/images/1234

b)相机拍摄:file:///mnt/sdcard/Pic.jpg

我找不到改变它的方法。

请参见更新

现在它会因为尝试上传“content://media/external/images/1234”而导致nullpointerexception而崩溃。 (只有相机,并非文件选择器。) 此外,如果选择器/相机关闭(返回按钮),我将无法再次调用它。

情况a)和b)100%工作正常,以下是有效的代码,包括如何知道是否调用了fileChooser或camera:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    if (resultCode != RESULT_OK) {
    /** fixed code **/
            //To be able to use the filechooser again in case of error
            mUploadMessage.onReceiveValue(null);
    /** fixed code **/
            return;
    }
    if (mUploadMessage==null) {
        Log.d("androidruntime","no mUploadMessage");
        return;
    }
    if (requestCode == FILECHOOSER_RESULTCODE) {
        Uri selectedImage= intent == null || resultCode != RESULT_OK ? null : intent.getData();
        Log.d("androidruntime","url: "+selectedImage.toString());

    }else if (requestCode == CAMERAREQUEST_RESULTCODE) { 
        if(mCapturedImageURI==null){
            Log.d("androidruntime","no mCapturedImageURI");
            return;
        }
      /** fixed code **/
        getContentResolver().notifyChange(mCapturedImageURI, null);
        ContentResolver cr = getContentResolver();
        Uri uriContent= Uri.parse(MediaStore.Images.Media.insertImage(getContentResolver(), photo.getAbsolutePath(), null, null));
        photo = null;
      /** fixed code **/
    }
    mUploadMessage.onReceiveValue(selectedImage);
    mUploadMessage = null;
}


    private static final int FILECHOOSER_RESULTCODE   = 2888;
    private static final int CAMERAREQUEST_RESULTCODE = 1888;
    private ValueCallback<Uri> mUploadMessage;
    private Uri mCapturedImageURI = null;

    protected class AwesomeWebChromeClient extends WebChromeClient{
        // Per Android 3.0+
        public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType){  
           /**updated, out of the IF **/
                            mUploadMessage = uploadMsg;
           /**updated, out of the IF **/
            if(boolFileChooser){ //Take picture from filechooser

                Intent i = new Intent(Intent.ACTION_GET_CONTENT);  
                i.addCategory(Intent.CATEGORY_OPENABLE);  
                i.setType("image/*");  
                MainActivity.this.startActivityForResult( Intent.createChooser( i, "Escoger Archivo" ), MainActivity.FILECHOOSER_RESULTCODE );  

            } else { //Take photo and upload picture
                Intent cameraIntent = new Intent("android.media.action.IMAGE_CAPTURE");
                File photo = new File(Environment.getExternalStorageDirectory(),  "Pic.jpg");
                cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                        Uri.fromFile(photo));
                mCapturedImageURI = Uri.fromFile(photo);
                startActivityForResult(cameraIntent, MainActivity.CAMERA_REQUEST);
            }
        }
        // Per Android < 3.0
        public void openFileChooser(ValueCallback<Uri> uploadMsg){
            openFileChooser(uploadMsg, "");
        }
        //Altre
        public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
            openFileChooser(uploadMsg, "");
        }



        /** Added code to clarify chooser. **/

        //The webPage has 2 filechoosers and will send a console message informing what action to perform, taking a photo or updating the file
        public boolean onConsoleMessage(ConsoleMessage cm) {        
            onConsoleMessage(cm.message(), cm.lineNumber(), cm.sourceId());
            return true;
        }
        public void onConsoleMessage(String message, int lineNumber, String sourceID) {
            Log.d("androidruntime", "Per cònsola: " + cm.message());
            if(message.endsWith("foto")){ boolFileChooser= true; }
            else if(message.endsWith("pujada")){ boolFileChooser= false; }
        }
        /** Added code to clarify chooser. **/



    }

更新1

我已经获取了 "content://media/external/images/xxx" 的uri格式,但是在尝试通过 "mUploadMessage.onReceiveValue(selectedImage);" 上传uri时,应用程序仍然崩溃。现在我得到了一个nullpointerexception。


更新2

已修复并正常工作。

我只在文件选择器情况下将 'ValueCallback uploadMsg' 设置为本地变量,因此当我尝试上传照片文件时,它总是抛出异常,因为它为空。一旦我从if-else语句中取出它,所有操作都正常工作。 先前的更新是处理文件上传的最简单方法。

如果取消相机/文件选择器意图(您必须在您的网页中处理它),我已经添加了 'mUploadMessage.onReceiveValue(null);',否则,您将无法再次启动INPUT字段(Intent)。


更新3

已添加代码部分至AwesomeChromeClient以区分选项,拍照或选择文件...这是我的方法,并按要求添加,我确信还有许多其他有效的方法可以实现,

代码现在100%功能正常。如果指示您想要图片或文件选择器,则可以执行此操作。


2
所有错误已修复,并且帖子已更新为可工作的代码。 - Jordi
代码看起来不错,对我非常有用,我还没有尝试过它,我正在努力找出变量“boolFileChooser”的值是在哪里设置的?另外发现一个拼写错误:我认为“MainActivity.CAMERA_REQUEST”应该是“MainActivity.CAMERAREQUEST_RESULTCODE”。 - user280109
我已经成功让我的代码运行了,非常感谢你的帮助。在网上尝试了很多其他的解决方案,但这个是最好的。干杯! - user280109
能否上传完整的代码?谢谢。 - Nizzy
我记得我也尝试过(使用了许多不同的源和代码尝试),但我不记得为什么会这样,但“它有效”:D 我不能发布完整的代码(应用程序和oncreate中的标准Webview的特定代码),但我添加了“boolFileChooser”部分...这是使其工作所缺少的部分,应该足够了。当我写这篇文章时,它还没有被实现。(我一直出门到现在,很抱歉没有早些回答你)。 - Jordi
显示剩余3条评论
3个回答

9
这是我如何从WebView输入字段实现相机上传和文件选择的方法:
以下是此重要主题的代码。已删除不相关的代码。
public class MainActivity extends Activity {

private WebView webView;
private String urlStart = "http://www.example.com/mobile/";

//File choser parameters
private static final int FILECHOOSER_RESULTCODE   = 2888;
private ValueCallback<Uri> mUploadMessage;

//Camera parameters
    private Uri mCapturedImageURI = null;

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

    webView = (WebView) findViewById(R.id.webView);

    webView.getSettings().setJavaScriptEnabled(true);
    webView.getSettings().setLoadWithOverviewMode(true);

    webView.getSettings().setAllowFileAccess(true);

    webView.loadUrl(urlStart);

    webView.setWebChromeClient(new WebChromeClient() {
        // openFileChooser for Android 3.0+
        public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) { 

            mUploadMessage = uploadMsg;

            try{
                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();
                String mCameraFilePath = cameraDataDir.getAbsolutePath() + File.separator +
                          System.currentTimeMillis() + ".jpg";
                mCapturedImageURI = Uri.fromFile(new File(mCameraFilePath));

                cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCapturedImageURI);

                Intent i = new Intent(Intent.ACTION_GET_CONTENT); 
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("image/*");

                Intent chooserIntent = Intent.createChooser(i, "Image Chooser");
                chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[] { cameraIntent });

                startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
              }
             catch(Exception e){
                 Toast.makeText(getBaseContext(), "Camera Exception:"+e, Toast.LENGTH_LONG).show();
             }
           }

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

    // For Android  > 4.1.1
        @SuppressWarnings("unused")
        public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
               openFileChooser(uploadMsg, acceptType);
           }



           public boolean onConsoleMessage(ConsoleMessage cm) {        
               onConsoleMessage(cm.message(), cm.lineNumber(), cm.sourceId());
               return true;
           }
           public void onConsoleMessage(String message, int lineNumber, String sourceID) {
               Log.d("androidruntime", "www.example.com: " + message);
             }
    });
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    // TODO Auto-generated method stub
    if(requestCode==FILECHOOSER_RESULTCODE)  
     {  

            if (null == this.mUploadMessage) {
                return;
            }

           Uri result=null;

           try{
                if (resultCode != RESULT_OK) {

                    result = null;

                } else {

                    // retrieve from the private variable if the intent is null
                    result = intent == null ? mCapturedImageURI : intent.getData(); 
                } 
            }
            catch(Exception e)
            {
                Toast.makeText(getApplicationContext(), "activity :"+e, Toast.LENGTH_LONG).show();
            }

            mUploadMessage.onReceiveValue(result);
            mUploadMessage = null;

     }
}

我希望这对某些人有所帮助 :)

2

问题已解决,一切正常。

只需查看WebChromeClient类的代码。 你会发现参数与以前不同。

public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
                       throw new RuntimeException("Stub!");
    }

解决方案:
您只需要按照此Github链接中的最新ChromeBrowser代码进行操作: Github上的Android Chrome浏览器代码 将相同的Github代码用于您的项目。
注意:在Android 4.4 KitKat版本中仍存在问题。除Android 4.4之外,其它版本的Android都可以正常运行。
这是因为这仍然是谷歌的一个未解决问题。请检查下面的链接: 单击Android 4.4 webview时未调用openFileChooser

2

已解决。如果有人需要,我的问题中包含了功能代码。

以下是问题的解决方案:

  1. Couldn't open camera/filechooser if I previously opened and cancelled:

    //inside onActivityResult
    if (resultCode != RESULT_OK) {
         mUploadMessage.onReceiveValue(null);
         return;
    }
    
  2. Get the "content://media/external/images/xxx" uri format, to upload the uri via "mUploadMessage.onReceiveValue(selectedImage);", avoiding a nullpointerexception

    //inside OnActivityResult
    getContentResolver().notifyChange(mCapturedImageURI, null);
    ContentResolver cr = getContentResolver();
    Uri uriContent = Uri.parse(android.provider.MediaStore.Images.Media.insertImage(getContentResolver(), photo.getAbsolutePath(), null, null));
    

Jordi,我使用了你的代码,我的应用程序不再崩溃,但是很遗憾图片无法上传到服务器。 - RaRa
因为我得到了“uriContent”为空。 - RaRa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接