Android Question Select file (like PDF) then upload via WebView or OkHttpUtils2

Erick Kalugdan

Member
Licensed User
I have a web app (written in PHP) which is accessed by a WebView in my B4A app.

I need the user to select a file (like PDF) from his phone and upload it to my web app via form POST as multipart/form-data.

Note that the file is outside my app's assets / internal directories.

I can't seem to make this work
I am not familiar with web sockets too.

Help please. Thanks! :)
 

drgottjr

Expert
Licensed User
Longtime User
what have you done to make it not work? zip and upload the project.

there are a couple other options which don't involve a webview, but they do involve filechooser or fileprovider as in the case of the referenced example. you need to understand how these work if your apps are routinely accessing resources outside of your apps's environment. there are a number of examples which serve as a good base for using these features. but let's take a look at what you've done so far.

web sockets do not apply here.
 
Upvote 0

Erick Kalugdan

Member
Licensed User
My app is using WebView and WebViewExtras to interact with my web app. I can't replace it. It's initiated like this:

B4X:
    WV1.Initialize("WV1")
    WV1.ZoomEnabled = False
    WV1.Color = Colors.ARGB(255,255,255,255)
    WVE.addWebChromeClient(WV1,"WVEvent")
    WVE.addJavascriptInterface(WV1, "APP")
    Activity.AddView(WV1, 0, 0, 100%x, 100%y)

The referenced example uses WebView like this:

B4X:
    Dim client As JavaObject
    client.InitializeNewInstance(Application.PackageName & ".main$MyChromeClient", Null)
    Dim jo As JavaObject = WebView1
    jo.RunMethod("setWebChromeClient", Array(client))

I can't do away with WVE as I need the app to interact with my web app via javascript.

Maybe a non-WebView method like FileChooser+OkHttpUtils2 would fix this? How? Or is it possible to do the referenced example with WVE?
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
if you can't do away with WVE, then you need webview. you can't open the door to non-webview approach if you need WVE. so case closed there.

zip the project and upload or make it available for download. somebody here will run it and see why you couldn't get it to work. you can't just say what you think is wrong or refer to snippets that you think are important. dozens of examples relating to POSTing multi-part here. nobody is looking to steal your app. you're trying to use an example that's already been posted for everyone to see anyway, so we're all in the same boat.

in theory, there is no reason why the example you refer to in your first post won't work with webviewextras. presumably, you made a mistake somewhere. no big deal. post the project.
 
Upvote 0

KMatle

Expert
Licensed User
Longtime User
1. Use this example to share a file to your app: https://www.b4x.com/android/forum/t...ple-image-s-shared-to-your-app.91612/#content
2. Read the file(s) into a byte array: https://www.b4x.com/android/forum/threads/b4x-bytes-to-file.70111/
3. Convert the byte array to a Base64 string (with StringUtils), put it to a map with the filename and generate a JSON string (via JSONGenerator)
4. Do a POST request to a php script:

B4X:
Dim Job As HttpJob
    Job.Initialize("", Me)
    Job.PostString("https://mywebsite.com/myfolder/myphpscript.php", JSONString)
    Wait For (Job) JobDone(j As HttpJob)
    Dim Result As String
    If j.Success Then
       Result = Job.GetString
    Else
        Log("Fail! See logs for details...")
    End If
    
    Job.Release


B4X:
<?php

$jsonstring = file_get_contents("php://input"); //retrieve the JSON string
$mymap = array();
$mymap=json_decode($jsonstring, true);

...

5. Convert the Base64 string back and store it into a file
 
Upvote 0

Erick Kalugdan

Member
Licensed User
I tried this and it worked. Notice that I needed to comment out the WVE parts (lines 8-9).

B4X:
    WV1.Initialize("WV1")

    Dim client As JavaObject
    client.InitializeNewInstance(Application.PackageName & ".main$MyChromeClient", Null)
    Dim jo As JavaObject = WV1
    jo.RunMethod("setWebChromeClient", Array(client))

    'WVE.addWebChromeClient(WV1,"WVEvent")
    'WVE.addJavascriptInterface(WV1, "APP")

    Activity.AddView(WV1, 0, 0, 100%x, 100%y)

But again, I need the WebView to interact with the web app's javascript. How do we go about this? :)

If I enable the WVE lines 8-9, I get this error:

Java:
java.lang.Exception: Sub showfile_chooser signature does not match expected signature.
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:215)
    at anywheresoftware.b4a.BA$2.run(BA.java:387)
    at android.os.Handler.handleCallback(Handler.java:914)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loop(Looper.java:224)
    at android.app.ActivityThread.main(ActivityThread.java:7560)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
 
Upvote 0

Erick Kalugdan

Member
Licensed User
1. Use this example to share a file to your app: https://www.b4x.com/android/forum/t...ple-image-s-shared-to-your-app.91612/#content
2. Read the file(s) into a byte array: https://www.b4x.com/android/forum/threads/b4x-bytes-to-file.70111/
3. Convert the byte array to a Base64 string (with StringUtils), put it to a map with the filename and generate a JSON string (via JSONGenerator)
4. Do a POST request to a php script:

B4X:
Dim Job As HttpJob
    Job.Initialize("", Me)
    Job.PostString("https://mywebsite.com/myfolder/myphpscript.php", JSONString)
    Wait For (Job) JobDone(j As HttpJob)
    Dim Result As String
    If j.Success Then
       Result = Job.GetString
    Else
        Log("Fail! See logs for details...")
    End If
   
    Job.Release


B4X:
<?php

$jsonstring = file_get_contents("php://input"); //retrieve the JSON string
$mymap = array();
$mymap=json_decode($jsonstring, true);

...

5. Convert the Base64 string back and store it into a file


Won't this increase the data by 4X that needs to be transmitted since we're converting bytes to base64?
 
Upvote 0

Erick Kalugdan

Member
Licensed User
The problem is that my example adds a custom WebChromeClient which will not work with WVE (unless you use two different WebViews).

Is it possible for one WebView to communicate and trigger another WebView? How?

Was thinking of having a WebView with WVE for my web app needs and another WebView just for the uploading of files function.
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
The problem is that my example adds a custom WebChromeClient which will not work with WVE (unless you use two different WebViews).

damn! sorry, i forgot that. however, in my own defense and at the risk of punishment, i would add that there is no direct conflict between webviewextras and the custom webchromeclient IF you don't use webviewextras' webchromeclient. i ran the example with webviewextras and the custom webchromclient and executed javascript via webviewextras without an issue.

it is true that, if i use the custom webchromeclient AND webviewextras webchromeclient (and declare the webviewextras' webchromeclient AFTER the custom webchromeclient, ShoeFileChooser would fail (as expected). but if i use the customchromeclient and webviewextras (without webviewextras webchromeclient),
there is no problem.

what the op uses a webchromeclient for is another question. but since he only reveals snippets of his code, it's difficult to say. webchromeclient doesn't allow all that much beyond onfilechooser and console.logging, and he has streesed in several posts that what he needs is webviewextras and javascript. he can have his cake and eat it.

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
and, if he has to have logging capability from the custom webchromeclient, he can add these 6 lines of inline code to the example's inline java:
B4X:
@Override
 public boolean onConsoleMessage (ConsoleMessage consoleMessage) {
   String logMessage = "CONSOLE LOG: " + consoleMessage.message() + " in (" + consoleMessage.sourceId() + " at Line: " + consoleMessage.lineNumber() + ")";
   processBA.raiseEventFromUI(this, "console", new Object[] {logMessage} );
   return true;
 }

if he creates a sub called "console" in main, he can see console messages. if he uses webchromeclient for other purposes, they could probably easily be added.
 
Last edited:
Upvote 0
Top