I am building a File Compressor app where the compression happens entirely inside the browser (using JavaScript in an HTML file). The file is generated successfully in the RAM as a Blob URL (blob:http://...).
What I have achieved so far:
I am using CustomWebView to load the HTML.
I successfully triggered the download using a Javascript Interface.
I converted the Blob to a Base64 string using the XMLHttpRequest method in the OnDownloadNeeded event.
I am using the KIO4_Base64 extension to decode and save the string.
The Problem:
I can save the file as a .txt or .zip successfully, but I am struggling with the File Path and Permissions on Android 10/11+.
If I use StringToFile, it sometimes fails or saves a corrupted file.
If I use StringToFileASD, it saves to the App Specific Directory (Private folder), but I want the user to see it in their main Downloads folder.
My Question:
Does anyone have a working block logic or a recommended extension that can:
Take a Base64 string (from the Blob).
Decode it to a binary ZIP file.
Save it directly to /storage/emulated/0/Download/ on newer Android versions (Scoped Storage)?
Any help or guidance on how to move the file from ASD to Downloads would be greatly appreciated!
I actually tried the direct download method first, but it seems to fail because I am dealing with a dynamically generated Blob URL (blob:http://...), not a standard HTTP link.
From what I've tested, the native Android Download Manager usually fails with Blob URLs on Android 11+ (Scoped Storage) because it can't access the browser's internal RAM. That is why I switched to the Base64 approach—it's the only way I found to successfully extract the data so far.
If you have a working Block logic that can download a blob: URL directly to the /Download/ folder on Android 11+ without using Base64, could you please share a screenshot? I would love to use a simpler method if it works!"
It is my current blocks... I have imported WebViewExtra extension but not understand how to implement its block... Can you give me block image that I need to complete it?(post deleted by author)
Do not mix webviewextra with customwebview, they are two different things altogether. If you want to know how to use webviewextra then please ask on that topic. Webviewextra uses the native webviewer, customwebview does it's own thing
Did you not see the blocks provided above by @vknow360 that show how you download a blob url with customwebview ?
On coustomWebViewer extension i can't get download helper extension... When I searched on internet I get 2 com.sunny.CustomWebView file.
One about 78kb and other about 295kb
When I import 295kb file I get only CustomWebView extension there is no DownloadHelper extension... But when I import 78kb file it have both 2 extention but when I change its block it not have all blocks.
After a lot of debugging, the app is almost finished, but I am stuck on one final issue regarding PDF downloads.
Here is the current status of the app: Images work perfectly: When a user compresses an image, it downloads correctly in its image format (e.g., .jpg). Other files work: Other generic file types download correctly as a safe .zip file. The Problem with PDFs: When a user compresses a PDF, the compression actually works on the server. However, the app downloads the result as a .zip file instead of a .pdf file.
The Current Workaround:
Currently, if the user manually opens their file manager and renames the downloaded file from file.zip to file.pdf, the PDF opens perfectly. The data is correct, but the extension is wrong.
Where I need help:
It seems my logic is failing to detect that the incoming file is a PDF, so it defaults to the .zip fallback. I have tried detecting the mimeType in the FileUploadNeeded event, but it seems the server might be sending a generic type instead of application/pdf.
Lable1 showing-
For image- Content://com.android.providers.media.documents/document.document.image%3A1000657020
For pdf- Content://com.android.providers.media.documents/document.document%3A1000657020
I am looking for guidance on the correct block design to handle this situation so that PDFs download automatically with the correct .pdf extension.
I have attached my current block setup below.
Could you share the javascript you use to generate the blob and convert the blob/url to base64? We might be able to improve the extensions if it works without needing a server.
I have this that works well, running the html file from the assets, locally on the app, and returns the base64 to the app using the webviewstring:
I want the app to save the downloaded file with the correct extension.
If the user compresses a PNG \rightarrow Save as .png
If the user compresses a WebP \rightarrow Save as .webp
Otherwise \rightarrow Save as .jpg
My Current Logic:
Inside the OnDownloadNeeded event, I am checking the mimeType variable:
IF mimeType contains "png" THEN extension = ".png"
ELSE IF mimeType contains "webp" THEN extension = ".webp"
ELSE extension = ".jpg"
The Problem:
When I test the app, the mimeType from the server often returns application/octet-stream (generic binary) instead of image/png.
Because of this, my IF condition fails, the logic falls into the ELSE block, and every single image is saved as a .jpg.
My Question:
Since mimeType is unreliable (giving octet-stream), what is the best way to detect the real file extension?
Should I be checking the contentDisposition or the url instead?
Does anyone have blocks or a regex pattern to extract the ".png" or ".webp" from the contentDisposition string?
Here is a screenshot of my current blocks that are failing:
You earlier indicated that you were having problems with this
Could be that, I believe, it needs to be an async function which handles and correctly returns a promise, as I show in my example above, which returns the base64 without the datauri, and works without a server (for local (in assets) html files). You can return just the base64 as a string to the app using webviewstring, then use a base64 extension to convert this to a binary file. You could also pass the generated filename with extension to the webviewstring for use in the binary file creation.
Wow, this approach looks like a game-changer! Bypassing the OnDownloadNeeded listener completely would solve so many of my permission headaches on Android 11+.
I am definitely going to try this 'Base64 to WebViewString' method. I just have two questions before I rewrite my code:
String Limit: Since this is a File Compressor app, the Base64 strings might be quite large (e.g., 5MB to 10MB). Have you tested if window.AppInventor.setWebViewString has a character limit or if it causes the app to freeze with large data?
The Async Logic: You mentioned it needs to be an async function to return the promise correctly. Could you share a small snippet of how you structure that async/await wrapper around the FileReader?
Thanks a lot for this suggestion, it really helps!
Technically, I believe it should be able to handle 5-10mb string sizes (you understand that base64 is generally @ 33% bigger than the original file). In practical terms you may hit a wall. Maybe you are just not compressing enough!!