1

I know I could potentially use the Cordova File plugin, but that seems to have multiple, platform-specific quirks (such as different directory structures) due to its low-level nature.

Today I found this answer that shows how to open a desktop browser's Save Dialog for the user to select where they want to save blob data, but it was code so I converted the idea to plain JavaScript:

function onSaveButtonClick() {
    var blob = new Blob(['abcd'], {type: 'text/plain'}),
        blobUrl = window.URL.createObjectURL(blob),
        a = document.createElement('a');

    document.getElementsByTagName('body')[0].appendChild(a);
    a.href = blobUrl;
    a.download = "file.name";
    a.click();
    a.innerHTML = blobUrl;
    window.setTimeout(function() {
        window.URL.revokeObjectURL(blobUrl);
    }, 1000);
}

CodePen here

(Also note that the download attribute of the <a> is not supported by IE, but Edge and in addition to that, IE seems to block the a.click() call.)

In the code above, setting the innerHTML is only for debugging purposes and for production use, you may want to set a.style.display = 'none' to hide the anchor altogether. I am also aware that the revokeObjectURL call in a timeout is a bit of a code smell, but it seems to work in desktop Chrome and Firefox.

Now the question is: How can I achieve the same in a Cordova app? I mean, letting the user choose the file save location via a stock Save As dialog so I don't have to add platform-specific code to my app? Running this code in a Cordova app on Android just displays the anchor but doesn't ask the user to save the file - even clicking the link (before or after the timeout) manually doesn't show it.

Update:

There is a related question here:

How to make Phonegap's (Cordova) File API work like File API in normal browser

The only answer requires the user to install a separate (though free) app and is therefore a no-go as I'd like my app to be self-contained.

FriendFX
  • 2,929
  • 1
  • 34
  • 63
  • it would depend on the android version, and if "browser" supported `a[download]`, `new Blob` and `window.URL`. i have a download lib that has some fallback methods that might work for you: https://github.com/rndme/download ... with some noted limitations, it can download files in rather old browsers. – dandavis Feb 24 '16 at 02:23
  • @dandavis Thanks for your lib, I tested it on Android 4.4 but it doesn't work either - I can see an `` element appear temporarily, but no download/save dialog is started. – FriendFX Feb 24 '16 at 02:48
  • sorry it didn't help. i guess you have to bite the bullet and do the file system thing... – dandavis Feb 24 '16 at 08:12
  • You can checkout [this](https://github.com/mikethedj4/Android-SaveFile-BinaryString/) Git Repo I made for PhoneGap Build - https://github.com/mikethedj4/Android-SaveFile-BinaryString/ ( Thanks to [@Whebcraft](http://stackoverflow.com/users/5112092/whebcraft) for the tip :) ) – Michael Schwartz Apr 27 '16 at 02:13

3 Answers3

2
   // Lets save a sample json data....

   function SaveData(fileName, Data) {
       window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSystem) {
    fileSystem.root.getFile(fileName, {
        create: true,
        exclusive: false
    }, function(fileEntry) {
        fileEntry.createWriter(function(writer) {
            writer.onwriteend = function() {
                alert("Data Saved");
            };
            writer.write(JSON.stringify(Data)); // if data type is not json use writer.write(Data);
        }, onError);
    }, onError);
}, onError);
   }

   function onError() {
alert("Error");
   }

   // function to create a directory if necessary
   function CreateDirectory(DirName) {
       window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function gotFS(fileSystem) {
    fileSystem.root.getDirectory(DirName, {
        create: true
    }, function gotDir(dirEntry) {});
}, function fail() {});
   }


   // Create Directory
   CreateDirectory("My Folder");

   var fileName = 'SavedFile.json'; // or file name and path e.g My Folder/SavedFile.json

   // json data
   var Data = '{ "id": "5001", "type": "None" },{ "id": "5002", "type": "Glazed" },{ "id": "5005", "type": "Sugar" }';

   // Save Data
   SaveData(fileName, Data);
Whebcraft
  • 168
  • 7
1

Use the Cordova file transfer plugin to download files https://github.com/apache/cordova-plugin-file-transfer

  function downloadFILE(filename, url){
   var ft = new FileTransfer();
var uri = encodeURI(url);

// /Documents/ folder in the users sdcard or phone storage if sdcard is not available
var downloadPath = fileSystem.root.fullPath + "/Documents/"+filename;

ft.onprogress = function(progressEvent) {
    if (progressEvent.lengthComputable) {
        var perc = Math.floor(progressEvent.loaded / progressEvent.total * 100);
    document.getElementById('myId').innerHTML = perc;
    } else {
        if($('#status').text() == "") {
    document.getElementById('myId').innerHTML = "Loading....";
        } else {
    document.getElementById('myId').innerHTML = ".";
        }
    }
};

ft.download(uri, downloadPath, 
function(entry) {
    document.getElementById('myId').innerHTML = "Download Complete! File Saved To" +entry.fullPath;
}, 
function(error) {
    alert('Crap something went wrong...');  
});
     }

Call with

 downloadFILE('npm-logo.svg', 'https://www.npmjs.com/static/images/npm-logo.svg');
Whebcraft
  • 168
  • 7
  • Thanks for your answer which is a nice example for the FileTransfer plugin. Regarding my question though, there are two problems with it: firstly it doesn't ask the user for where to save the file and secondly it requires the file to be downloaded from a server (I specifically asked for client-side data to be saved). As an aside, where is the `fileSystem` object defined? – FriendFX Feb 24 '16 at 22:46
  • ok I understand you better. Lets use this example, lets say we generated a simple client side data and we want to save that data on the users device in your custom directory (allowing the user to select a directory I really dont have an idea how to do that). see the full code below .... – Whebcraft Feb 26 '16 at 22:11
0

Mobile OSes are generally designed to hide their file system from the user. Downloaded files goe to OS/App-specific "Downloads" folder without the user being able to choose. Also OSes mostly do not come with a built in file-browser to randomly pick / select files to open. The Downloads-folder however is usually somehow reachable (e.g using the downloads shortcut) and hence those files can be openened by the user. I know this is not a solution, but may shed some light on why there probably does not exist a real solution for your question that satisfies all of your needs.