Android Question Download files from WebView (local storage)

max123

Active Member
Licensed User
Hi all,

I want to explain what I do with my project but do not confuse it with sophysticated things, the question here is just one, how to download a file from a WebView, how to handle it so when I press on the link the file should download.

Suppose you have a page with download link, how to download a file?

I've developed an app that permits to manage threejs javascript 3D library and WebGL.

This app have 2 pages in a TabHost:
- Page1 There is a big EditText inside a ScrollView2D, here I can write HTML + Javascript code to execute. There are options to Save and Load HTML files.
- Page2 There is a WebView, when I press the TabHost page2 the HTML file is saved to DirRootExternal, then loaded inside a WebView where the 3D scene is rendered.

To be more precise I recreated the threejs distribution (just folders and files needed mantaining original structure) inside the app folder (in DirRootExternal) where the library search all files like JS imports, textures, models etc.

Initially I had problem to reference files URL pointing to DirRootExternal but then I added this sub
B4X:
Sub AllowUniversalAccessFromFileURLs(wv As WebView)
    Dim jo As JavaObject = wv
    Dim settings As JavaObject = jo.RunMethod("getSettings", Null)
    Dim r As Reflector
    r.Target = settings
    r.RunMethod2("setAllowUniversalAccessFromFileURLs", True, "java.lang.boolean")
End Sub
and now I'm able to import any file I need, this worked well and I'm really impressed on what this library can do, I've managed to create some simples and complex scenes, import 3D models like STL, OBJ, 3DS, Collada, 3MF, PLY, apply textures, mipmapping etc.

------

Now I've the opposed problem, that library have Exporters, so I can create objects starting from primitives, boxes, spheres, cylinders etc. manage these with boolean operations like subtract, union, intersect, then export the result as OBJ, STL and other 3D file formats.

My goal is to export objects in STL file format so I can import these inside a 3D printing slicing program like RepetierHost, Cura etc., slice and print with my 3D printer.

To start with, this is a simple Javascript code that add to the scene a simple cube (screenshot attached) and then add to the page 2 links, one to export it as STL in ASCII format and one to export to Binary format.
JavaScript:
<!DOCTYPE html>

<html>

    <head>
        <meta charset="utf-8">
        <title>ThreeJs</title>
        <!-- This is important to get the correct canvas size on mobile devices -->
        <meta name='viewport' content='width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0'/>
        <style>
            /* Set margin to 0 and overflow to hidden, to go fullscreen */
            body { margin: 0; overflow: hidden; }
        </style>
    </head>

    <body>
 
        <!-- Import three.js  Note that this can be changed with minified three.min.js -->
        <script src="../build/three.js"></script>

        <!-- [STEP 1] INSERT HERE IMPORTS. HERE A TEMPLATE, JUST EDIT AND/OR REMOVE, COMMENT, UNCOMMENT AS NECESSARY -->

      <script src="js/controls/OrbitControls.js"></script>
      <script src="js/libs/stats.min.js"></script>
      <script src="js/libs/dat.gui.js"></script>    <!-- minified dat.gui.min.js can be used instead -->
 
      <script src="js/exporters/STLExporter.js"></script>  <!-- IMPORT STL EXPORTER -->

 <!-- <script src="js/utils/GeometryUtils.js"></script> -->
 <!-- <script src="js/utils/BufferGeometryUtils.js"></script> -->
 <!-- <script src="js/loaders/STLLoader.js"></script> -->
 
        <script>

  ///// [STEP 2] INSERT HERE JAVASCRIPT CODE /////

            let scene, camera, renderer, exporter, mesh, controls;

            const params = {
                exportASCII: exportASCII,
                exportBinary: exportBinary
            };

            init();
            animate();

            function init() {

                camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 5000 );
                camera.position.set( 200, 100, 200 );

                scene = new THREE.Scene();
                scene.background = new THREE.Color( 0xa0a0a0 );

                exporter = new THREE.STLExporter();  // HERE THE STL EXPORTER

                // Lights

                const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444 );
                hemiLight.position.set( 0, 200, 0 );
                scene.add( hemiLight );

                const directionalLight = new THREE.DirectionalLight( 0xffffff );
                directionalLight.position.set( 0, 200, 100 );
                scene.add( directionalLight );

                // Ground plane

                const ground = new THREE.Mesh( new THREE.PlaneGeometry( 500, 500 ), new THREE.MeshPhongMaterial( { color: 0x0000FF, depthWrite: false } ) );
                ground.rotation.x = - Math.PI / 2;
                scene.add( ground );

                const grid = new THREE.GridHelper( 500, 20, 0x000000, 0x000000 );
                grid.material.opacity = 0.2;
                grid.material.transparent = true;
                scene.add( grid );

                // Export mesh

                const geometry = new THREE.BoxGeometry( 50, 50, 50 );   // THE BOX TO BE EXPORTED, 50x50x50 MILLIMETERS
                const material = new THREE.MeshPhongMaterial( { color: 0x00ff00 } );

                mesh = new THREE.Mesh( geometry, material );
                mesh.position.y = 25;
                scene.add( mesh );

                // WebGL renderer

                renderer = new THREE.WebGLRenderer( { antialias: true } );
                renderer.setPixelRatio( window.devicePixelRatio );   // Adjust with DPI
                renderer.setSize( window.innerWidth, window.innerHeight );
                document.body.appendChild( renderer.domElement );

                // OrbitControls

                controls = new THREE.OrbitControls( camera, renderer.domElement );
                controls.target.set( 0, 25, 0 );
                controls.update();

                // Resize event listener

                window.addEventListener( 'resize', onWindowResize );

                // GUI

                const gui = new dat.GUI();

                gui.add( params, 'exportASCII' ).name( 'Export STL (ASCII)' );
                gui.add( params, 'exportBinary' ).name( 'Export STL (Binary)' );
                gui.open();
            }

            function onWindowResize() {
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
                renderer.setSize( window.innerWidth, window.innerHeight );
            }

            function animate() {
                requestAnimationFrame( animate );
                renderer.render( scene, camera );
                controls.update();
            }

            function exportASCII() {
                Log ("Export ASCII STL file");
                const result = exporter.parse( mesh );
                saveString( result, 'box.stl' );
            }

            function exportBinary() {
                Log ("Export bynary STL file");
                const result = exporter.parse( mesh, { binary: true } );
                saveArrayBuffer( result, 'box.stl' );
            }

            const link = document.createElement( 'a' );  // CREATE DOWNLOAD LINK
            link.style.display = 'none';
            document.body.appendChild( link );

            function save( blob, filename ) {
                Log ("Save the file: " + filename);
                link.href = URL.createObjectURL( blob );
                link.download = filename;
                link.click();
            }

            function saveString( text, filename ) {
                Log ("Save string: " + filename);
                save( new Blob( [ text ], { type: 'text/plain' } ), filename );
            }

            function saveArrayBuffer( buffer, filename ) {
                Log ("Save array buffer: " + filename);
                save( new Blob( [ buffer ], { type: 'application/octet-stream' } ), filename );
            }

            function Log (text) {
               console.log(text);
            }

    //////////// END OF JAVASCRIPT CODE ////////////

        </script>
    </body>
</html>
The problem here is that by clicking on these links nothing happen, threejs library should save files in the root where HTML file is, but I need to manage a WebView to do downloads. I've tried in a lots of ways but without success, probably I missed something basic.

You can see yourself online on
https://threejs.org/examples/misc_exporter_stl.html

Please, can someone help me figure how to do it in the right way ?
Possibly that works on older and newer Android versions so the app can work on older and newer devices.

Many thanks
 

Attachments

  • 20220607_180139.jpg
    20220607_180139.jpg
    107.3 KB · Views: 25
  • 20220607_172501.jpg
    20220607_172501.jpg
    139.1 KB · Views: 26
  • 20220607_172803.jpg
    20220607_172803.jpg
    176.4 KB · Views: 24
  • 20220607_173058.jpg
    20220607_173058.jpg
    298.1 KB · Views: 29
  • 20220607_173616.jpg
    20220607_173616.jpg
    131.3 KB · Views: 24
  • 20220607_173828.jpg
    20220607_173828.jpg
    102.6 KB · Views: 24
  • 20220607_174032.jpg
    20220607_174032.jpg
    139.5 KB · Views: 25
Last edited:

max123

Active Member
Licensed User
I can upload a zip file yes, however, you need to download a threejs distribution and copy manually to the app folder,, the project has nothing of special here, I already posted a relevant code (that is html+js), in B4A I just added a WebView from designer and by code I load the html file that I've posted (the same of that online).

The project even do some other things because here need to import threejs library, so copy some files from asset unzip a file in app folder and because I wanted make it as a tutorial for threejs library, it copy some demo html files.

The main problem is that threejs dustribution is a very big file, 306 MB, to test it I've removed some unused models, textures etc, but still remain 180 MB, I copy it from asset, this worked but sure not right way.

But this is not relevant with this and maybe can confuse....

Are just download links, nothing of special.

Suppose you have a webpage and a simple download link to download a file, how do you handle it to download ? Only this I need, no other...

As you can see javascript put 2 download links in the page.
 
Last edited:
Upvote 0

max123

Active Member
Licensed User
Maybe this post confused some peoples that read it, I wanted to explain what I do with threejs, but this is not anyway relevant.

The question here is just one, how to download a file from a WebView, how to handle it so when I press on the link the file should download.
 
Upvote 0

max123

Active Member
Licensed User
post a link to the real url so we can see if the links there?
Hi @DonManfred I'm sorry I don't understand what you mean by this.

The link here is local, just javascript handle file stream to be downloaded and webview should download. The first works because the same html file inline can download both 2 files (tested on Firefox) the file is saved to Downloads folder, the second should be handled from B4A, but I do not know how.

I even added a javascript interface to a webview with WebViewExtras, but not sure if this really needed, to handle download I need to call manually by Javascript a B4A sub that handle download with HttpUtils2 ?
 
Last edited:
Upvote 0

max123

Active Member
Licensed User
I've tried to call B4A sub DownloadAndSaveFile from javascript and pass to B4A the URL, but it is a blob with this syntax and okHttpUtils2 refuse it:

blob:file:///a long hex number

from javascript I've used this:
JavaScript:
function save( blob, filename ) {
   console.log ("Save the file: " + filename);
   link.href = URL.createObjectURL( blob );
   link.download = filename;
   link.click();

   B4A.CallSub("DownloadAndSaveFile", true, link.href);
}
the B4A interface was created with WebViewExtras
 
Last edited:
Upvote 0
Top