Android does not allow native apps like Phonegap-based apps to write binary files. A common application is converting Base64 Strings to Images. So, how do you go about this problem?
3 Answers
The solution; This plugin that converts a Base64 PNG String and generates an image to the sdCard. Let's go!
1. The Base64 Decoder
Get this blazing fast Base64 encode/decoder class called MiGBase64. Download it from SourceForge. Create a folder called 'util' within your project's src/ folder. Place the downloaded class there.
2. The java
Create a folder called 'org/apache/cordova' within your project's src/ folder. Create in it a Java file called "Base64ToPNG.java" with the following source code.
package org.apache.cordova;
/**
* A phonegap plugin that converts a Base64 String to a PNG file.
*
* @author mcaesar
* @lincese MIT.
*/
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.cordova.api.Plugin;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import android.os.Environment;
import java.io.*;
import org.json.JSONException;
import org.json.JSONObject;
import util.Base64;
public class Base64ToPNG extends Plugin {
@Override
public PluginResult execute(String action, JSONArray args, String callbackId) {
if (!action.equals("saveImage")) {
return new PluginResult(PluginResult.Status.INVALID_ACTION);
}
try {
String b64String = "";
if (b64String.startsWith("data:image")) {
b64String = args.getString(0).substring(21);
} else {
b64String = args.getString(0);
}
JSONObject params = args.getJSONObject(1);
//Optional parameter
String filename = params.has("filename")
? params.getString("filename")
: "b64Image_" + System.currentTimeMillis() + ".png";
String folder = params.has("folder")
? params.getString("folder")
: Environment.getExternalStorageDirectory() + "/Pictures";
Boolean overwrite = params.has("overwrite")
? params.getBoolean("overwrite")
: false;
return this.saveImage(b64String, filename, folder, overwrite, callbackId);
} catch (JSONException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage());
} catch (InterruptedException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.ERROR, e.getMessage());
}
}
private PluginResult saveImage(String b64String, String fileName, String dirName, Boolean overwrite, String callbackId) throws InterruptedException, JSONException {
try {
//Directory and File
File dir = new File(dirName);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dirName, fileName);
//Avoid overwriting a file
if (!overwrite && file.exists()) {
return new PluginResult(PluginResult.Status.OK, "File already exists!");
}
//Decode Base64 back to Binary format
byte[] decodedBytes = Base64.decode(b64String.getBytes());
//Save Binary file to phone
file.createNewFile();
FileOutputStream fOut = new FileOutputStream(file);
fOut.write(decodedBytes);
fOut.close();
return new PluginResult(PluginResult.Status.OK, "Saved successfully!");
} catch (FileNotFoundException e) {
return new PluginResult(PluginResult.Status.ERROR, "File not Found!");
} catch (IOException e) {
return new PluginResult(PluginResult.Status.ERROR, e.getMessage());
}
}
}
3. The Javascript
Write this JavaScript as Base64ToPNG.js to your project's www folder. DONT forget to include a reference to it in your html files.
/**Works on all versions prior and including Cordova 1.6.1
* by mcaesar
* MIT license
*
*/
(function() {
/* This increases plugin compatibility */
var cordovaRef = window.PhoneGap || window.Cordova || window.cordova; // old to new fallbacks
/**
* The Java to JavaScript Gateway 'magic' class
*/
function Base64ToPNG() { }
/**
* Save the base64 String as a PNG file to the user's Photo Library
*/
Base64ToPNG.prototype.saveImage = function(b64String, params, win, fail) {
cordovaRef.exec(win, fail, "Base64ToPNG", "saveImage", [b64String, params]);
};
cordovaRef.addConstructor(function() {
if (!window.plugins) {
window.plugins = {};
}
if (!window.plugins.base64ToPNG) {
window.plugins.base64ToPNG = new Base64ToPNG();
}
});
})();
4. The plugins.xml file
Add the following to res/xml/plugins.xml file
<plugin name="Base64ToPNG" value="org.apache.cordova.Base64ToPNG"/>
5. Finally, HTML examples and the parameters
<button onclick="test();">No optional params required, Cowboy.</button> </br>
<button onclick="test2();">Make PNG with some parameters</button>
<script src="Base64ToPNG.js" type="text/javascript"></script>
<script type="text/javascript">
//May have a mime-type definition or not
var myBase64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="//a red dot
function test(){
//Illustrates how to use plugin with no optional parameters. Just the base64 Image.
window.plugins.base64ToPNG.saveImage(myBase64, {},
function(result) {
alert(result);
}, function(error) {
alert(error);
});
}
//No mimetype definition example
var myOtherBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
function test2(){
//Shows how to use optional parameters
window.plugins.base64ToPNG.saveImage(myBase64, {filename:"dot.png", overwrite: true},
function(result) {
alert(result);
}, function(error) {
alert(error);
});
}
</script>
Parameters
- filename: Name of the file to be generated. By default the same as the one in url.
- folder: Name of the directory to generate the file to. By default "sdcard/Pictures"
overwrite: If the file already exists, replace it. By default false.
I hope this answers some bothering questions. Happy coding!

- 969
- 2
- 12
- 28
-
1thank you. You saved me hours of figuring out how this would work!!. I did some little modifications to support mp3 files instead of PNG. Thanks again!!!! – ElHacker Nov 15 '12 at 01:25
-
@mcaesar Thanks alot, got everything ready except for one line: byte[] decodedBytes = Base64.decode(b64String.getBytes()); gives me the following error: The method decode(byte[], int) in the type Base64 is not applicable for the arguments (byte[]) Since I am no pro in Java could you help me out? Would appreciate it. – MarkSmits Apr 25 '13 at 19:20
-
Hi @Mark. Please make sure that your base64 string is well formatted. See examples I provided. Thank you. – mukama Apr 26 '13 at 11:27
-
@mcaesar Got it working, the line prevented me from compiling my code. I've changed it to: `byte[] decodedBytes = Base64.decode( b64String.getBytes(), Base64.DEFAULT );` cause it required the extra parameter. I also had to remove the `data:image/png;base64,` part in Javascript cause the images were corrupt with it or substract 22 instead of 21 characters in the plugin. Maybe replacing the specific part of the string with nothing is more solid option, the con: you have to check per specific file type, jpg or png. Thanks for the response. – MarkSmits Apr 29 '13 at 07:16
-
@mcaesar I found a bug in your code: you check if base64 starts with 'data:png' but one line above that you set base64 = ''; :) – REJH Jul 28 '13 at 07:36
-
I'm using newest PhoneGap (coding in eclipse) and got this error: `Error in an XML file: aborting build.` (btw. I had to create plugins.xml file - is that ok that I haven't that file before?) – lukaszkups Dec 05 '13 at 14:53
-
can you post a git repos that follow cordova spec? – Reign.85 Jul 27 '14 at 10:56
Fo anybody wanting to use this with kineticjs, the following works a treat:
function saveCanvas() {
$('#save').bind( $bind, function(){
stage.toDataURL({
callback: function(dataUrl){
window.plugins.base64ToPNG.saveImage(dataUrl.substr(22,dataUrl.length), {},
function(result) {
alert(result);
}, function(error) {
alert(error);
}
);
},
mimeType: 'image/png',
quality: 0.5
});
});
}

- 4,056
- 2
- 37
- 70
This solution only works when feeding it a CLEAN Base64 string. In other words, the "data:image/png;base64," part should be removed or the Base64 decoder fill fail, causing a nullpointer error when writing the file.
Also I noticed that the image does not show up in the Gallery but it is stored correctly on the SD card. When I download it to my PC I can open it just fine. Not sure what that is about.
Thanks for the work!

- 1,215
- 12
- 17
-
Thanks for the corrections. Edited the java to support full "data:image/png;base64". As for the picture not appearing in the gallery, android indexes media files only when storage drives change state eg from on to off. – mukama Jul 30 '12 at 19:03
-
@mcaesar I have 3 questions about your plugin: 1. I found that the parameter "folder" should be: sdcard/somedirectory_name instead of "somedirectory_name". 2.I set a break point into the instrument: byte[] decodedBytes = Base64.decode(b64String.getBytes()); in Base64ToPNG.java. And I found the Base64.decode(~) returns a NULL. 3. In your description, you said "Place the downloaded class there.",but I only get the file: Base64.java. Is anything wrong? I really need your plugin to convert my canvas to .png file, saving it in the local system. Please give me some guidance. – Alston Oct 10 '12 at 10:08
-
@mcaesar You have mentioned that you correct the java file to support the full "data:image/png;base64", but I'm afraid you forgot to upload the edited file. Thank u. – Alston Oct 10 '12 at 10:23
-
@Stallman, 1) The default folder is 'Environment.getExternalStorageDirectory() + "/Pictures";' which translates to "sdcard/Pictures". 2) The decoder seems to work just fine but in case of any issues reffer to the original code(http://sourceforge.net/projects/migbase64/) or get an alternative decoder(http://www.codecodex.com/wiki/Encode/Decode_to/from_Base64). 3)Yes. The unzipped Base64.java goes to "src/Base64.java". I understand that its a long solution but if you follow along step-by-step, it will work out. All the best. – mukama Oct 11 '12 at 15:46
-
@mcaesar Thank u for the response. The add-in can work for some base64 string, saving it to a png file in the local system. But when the string is canvas.toDataURL();, it just can't work. Is anything wrong with the plugin? Or it's my code's problem. – Alston Oct 12 '12 at 11:35
-
@Stallman, unfortunately, android's browser has limited functionality and "canvas.toDataURL();" will NOT work. ( More about this here: http://code.google.com/p/android/issues/detail?id=7901 ). ( Solution: http://code.google.com/p/todataurl-png-js/ ). – mukama Oct 13 '12 at 10:26
-
@mcaesar I'll try on plugin, thank u. However, my working environment is PhoneGap+ Android system, and I assign the returned string created by canvas.toDataURL("image/png"); to image.src and it can display the canvas drawing.//the variable image is a DOM object:
. Do you have any idea? – Alston Oct 13 '12 at 12:30
-
@mcaesar Maybe you can track my issue: http://stackoverflow.com/questions/12873382/is-the-string-returned-by-canvas-todataurlimage-png-really-a-encoded-64-base – Alston Oct 13 '12 at 13:10
-
@mcaesar May I ask you that do you have the version: "Convert the canvas to ""JPG"" file format?" Thank u. – Alston Nov 04 '12 at 12:12
-
@Stallman Its the same story as with PNG. Just make sure you dont forget the extension. :) – mukama Nov 13 '12 at 11:56
-
@mcaesar Do you mean that if I write filename:XXX.jpg, the generated pic will be a JPG format file? I thought the plugin change the base64 string to .PNG file only. – Alston Nov 14 '12 at 12:21