Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

App cache iOS PhoneGap

On Android I used

file:///storage/sdcard0/Android/data/my.app.id/cache/

to download (FileTransfer) some images and then show them in my html. Images and everything else inside this folder gets removed when deleting my app, which is exactly what I want.

How to achieve this on iOS?

I tried with

file:///var/mobile/Applications/<GUID of app>/Documents/

since this is what I got when requesting file system, but the images won't even download (error code 1). Any ideas? I found out more about my error: it's the same error as stated in this question. (Could not create path to save downloaded file - Cocoa Error 512)

Thanks


More info: Relevant plugins I use are

<gap:plugin name="org.apache.cordova.file" />
<gap:plugin name="org.apache.cordova.file-transfer" />

and features:

<feature name="File">
    <param name="android-package" value="org.apache.cordova.file.FileUtils" />
</feature>
<feature name="File">
    <param name="ios-package" value="CDVFile" />
</feature>
<feature name="FileTransfer">
    <param name="ios-package" value="CDVFileTransfer" />
</feature>

I download images successfully in Android with:

var fileTransfer = new FileTransfer();    
var uri = encodeURI("myserverurl/"+fileName); 
var filePath = appCachePath+fileName;  
        fileTransfer.download(
            uri,
            filePath,
            function(entry) {
                alert("download complete: " + entry.fullPath); 
            },
            function(error) {  
                alert("download error source/target/code:\n" + error.source +" \n||| "+error.target+" \n||| "+error.code); 
            }  
        );

I download them AFTER I successfully get FS with

function onDeviceReady() { 
    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, fail); 
    if(device.platform === 'Android'){
        cacheFolderSubPath = "Android/data/id.app.my/cache/";  
    }
    else{
        cacheFolderSubPath =  ""; //  for iOS what ??   
    }  
}

function gotFS(fileSystem) {    
    var nturl = fileSystem.root.toNativeURL();  // returns cdvfile://localhost/persistent/ for both iOS and Android 
    window.resolveLocalFileSystemURL(nturl+cacheFolderSubPath, onResolveSuccess, onResolveFail);  
}

function onResolveSuccess(fileEntry) { 
     // this is "file:///..." string mentioned in the question above.
    appCachePath = fileEntry.toNativeURL()+"/";  
    // etc etc etc ... (use this appCachePath with download code above)
}
like image 309
trainoasis Avatar asked Jul 07 '14 12:07

trainoasis


2 Answers

After days of struggling with this I found the not-so-obvious but very easy solution.

Instead of using

file:///var/mobile/Applications/<GUID of app>/Documents/

to download files on iOS, I used just plain

cdvfile://localhost/persistent/ 

that I got from

fileSystem.root.toNativeURL(); // same for Android and iOS

This downloaded my file successfully to

file:///var/mobile/Applications/<GUID of app>/Documents/

and this is also the src path I used to display images in HTML.


Just to clarify what happened in my case: ( when using resolveLocalFileSystemURL() )

  • On Android:

    cdvfile://localhost/persistent/ -> file:///storage/sdcard0/
    

    I had to add

    Android/com.my.appid/cache/
    

    manually and if I removed the app, files got removed as well which is OK.

  • On iOS:

    cdvfile://localhost/persistent/ -> file:///var/mobile/Applications/<GUID of app>/Documents/
    

    Here I didn't need to add anything, persistent storage was already pointing to my app's own 'cache' which is in this case in Documents folder. This gets removed with the app as well which is correct.

like image 106
trainoasis Avatar answered Sep 24 '22 21:09

trainoasis


Error code 1 means that the file was not found.

You need to get the application directory that your app is stored in. That is where your files will be e.g.

var imagePath = 'file:///var/mobile/Applications/E50F2661-798B-4F7D-9E6D- BCC266286934/tmp/cdv_photo_011.jpg'

FileErrors = {
    1:   'File not found',
    2:   'Security error',
    3:   'Action Aborted',
    4:   'File not readable',
    5:   'Encoding error',
    6:   'No modification allowed',
    7:   'Invalid state error',
    8:   'Syntax error',
    9:   'Invalid modification error',
    10:   'Quota Exceeded',
    11:   'Type mismatch',
    12:  'Path exists'
};

// extract the application directory from the full path
findApplicationDirectory = function (imagePath) {
  var dir
      , index = fullPath.indexOf('file:///') > -1 ? 6 : 4

  if(fullPath.indexOf('/') > -1){
    fullPath = fullPath.split('/')

    if(fullPath[index]){
      dir = fullPath[index]
    }
  }

  return dir
}

// downloads a file from the server
// @param {string} url
// @param {string} filePath
// @param {function} callback
downloadFile = function (url, fileName, callback){

    getApplicationDirectory(function (applicationDirectory){

        var downloader = new FileTransfer()
                , fileLocation =  findApplicationDirectory(imagePath) + '/' + fileName;

        downloader.download(
            url,
            fileLocation,
            function(entry) {
                console.log("-- downloadFile: download complete: " + entry.fullPath);

                if(typeof callback == 'function'){
                    console.log('-- getFile: calling back');
                    callback.call(this, entry.fullPath);
                }
            },
            function(error) {
                console.log("-- downloadFile: fileLocation " + fileLocation);
                console.log("-- downloadFile: download error source " + error.source);
                console.log("-- downloadFile: download error target " + error.target);
                console.log("-- downloadFile: download error code " + FileErrors[error.code]);
            }
        );
    });
};

Make sure you have access to the Documents folder on iOS by adding UIFileSharingEnabled= "Application supports iTunes file sharing" to your project plist. Then you can see the contents of your App's documents folder by running your app on a device, connecting it to iTunes and under you device's list of apps finding your app.

To copy any file into the /Documents directory you can use the following

// move photo to documents directory
// @param {string} imagePath
// @return {string} newImagePath
// usage savePhotoToDocuments(imagePath).done(function (newImagePath) { }).fail(function () { })
savePhotoToDocuments = function (imagePath){

    function onFileSystemSuccess(fileSystem) {
        console.log('onFileSystemSuccess: fileSystem.name ' + fileSystem.name);

        window.resolveLocalFileSystemURI(directoryPath, onGetDocumentDirectorySuccess, onGetDocumentDirectoryFail)
    }

    function onFileSystemFail(error){
        console.log('onFileSystemFail: ' + FileErrors[error.code])

        promise.reject(error)
    }

    function onResolveSuccess(fileEntry) {
        imageFileEntry = fileEntry

        imageFileEntry.copyTo(documentDirectory, newImageName, onCopyToSuccess, onCopyToFailed)
        console.log('onResolveSuccess: ' + fileEntry);
    }

    function onResolveFail(error) {
        console.log('onResolveFail: ' + FileErrors[error.code]);

        promise.reject(error)
    }

    function onGetDocumentDirectoryFail (error){
        console.log('onGetDocumentDirectoryFail: ' + FileErrors[error.code]);

        promise.reject(error)
    }

    function onGetDocumentDirectorySuccess (directoryEntry){
        documentDirectory = directoryEntry

        console.log('onGetDocumentDirectorySuccess')

        window.resolveLocalFileSystemURI(imagePath, onResolveSuccess, onResolveFail)
    }

    function onCopyToSuccess (fileEntry) {
        console.log('-- savePhotoToDocuments: onCopyToSuccess')

        promise.resolve(newImagePath)
    }

    function onCopyToFailed (error){
        console.log('-- savePhotoToDocuments: onCopyToFailed - ' + FileErrors[error.code])

        // invalid modification error
        // meaning the file already exists
        if(error.code != 9){
            promise.reject(error)
        } else {
            promise.resolve(newImagePath)
        }
    }

    var imageFileEntry
            , documentDirectory
            , promise = $.Deferred()
            //, imagePath = 'file:///var/mobile/Applications/E50F2651-798B-4F7D-9E6D-BCC266286934/tmp/cdv_photo_011.jpg'
            , imageName = imagePath.substring(imagePath.lastIndexOf('/')+1)
            , newImageName = Date.now() + '_' + imageName
            , directoryPath = 'file:///var/mobile/Applications/' + findApplicationDirectory(imagePath) + '/Documents'
            , newImagePath = directoryPath + '/' + newImageName

    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFileSystemSuccess, onFileSystemFail)

    return promise
}
like image 38
Peter Avatar answered Sep 23 '22 21:09

Peter