How to iterate through file system directories and files using javascript?

大憨熊 提交于 2019-12-07 13:08:42

问题


I'm using Javascript to write an application that will be used with Phonegap to make an Android application. I'm using the Phonegap File API to read directories and files. The relevant code is shown below:

document.addEventListener("deviceready", onDeviceReady, false);

// PhoneGap is ready
//
function onDeviceReady() {
    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFileSystemSuccess, fail);
}

function onFileSystemSuccess(fileSystem) {
    fileSystem.root.getDirectory("/sdcard", {create: false, exclusive: false}, getDirSuccess, fail);
}

function getDirSuccess(dirEntry) {
    // Get a directory reader
    var directoryReader = dirEntry.createReader();

    // Get a list of all the entries in the directory
    directoryReader.readEntries(readerSuccess,fail);
}

var numDirs = 0;
var numFiles = 0;

function readerSuccess(entries) {
    var i;
    for (i=0; i<entries.length; i++) 
    {
        if(entries[i].isFile === true)
        {
        numFiles++;
        entries[i].file(fileSuccess,fail);
        }
        else if (entries[i].isDirectory === true)
        {
        numDirs++;
        getDirSuccess(entries[i]);
        }
    }
}

So as of now, the program works fine. The reader will read the contents of the /sdcard directory..if it encounters a file, it will call fileSuccess (which I've excluded in the code for brevity), and if it encounters another directory, it will call getDirSuccess again. My question is this: How can I know when the entire /sdcard directory is read? I can't think of a good way of accomplishing this without going through the /sdcard directory more than one time. Any ideas are appreciated, and thank you in advance!


回答1:


+1 on a good question since I have to do this anyway myself. I would use the old setTimeout trick. Once the cancel doesn't occur anymore, you know you are done and can fire your event, but just ensure its only fired once.

Here's what I mean and I've named the variables long simply to be more readable (not my style)...

// create timeout var outside your "readerSuccess" function scope
var readerTimeout = null, millisecondsBetweenReadSuccess = 100;

function readerSuccess(entries) {
    var i = 0, len = entries.length;
    for (; i < len; i++) {
        if (entries[i].isFile) {
            numFiles++;
            entries[i].file(fileSuccess,fail);
        } else if (entries[i].isDirectory) {
            numDirs++;
            getDirSuccess(entries[i]);
        }
        if (readerTimeout) {
            window.clearTimeout(readerTimeout);
        }
    }
    if (readerTimeout) {
        window.clearTimeout(readerTimeout);
    }
    readerTimeout = window.setTimeout(weAreDone, millisecondsBetweenReadSuccess);
}

// additional event to call when totally done
function weAreDone() {
   // do something
}

So the logic in this is you keep cancelling the "weAreDone" function from being called as you are reading through stuff. Not sure if this is the best way or more efficient but it would not result in more than one loop given the appropriate "millisecondsBetweenReadSuccess".




回答2:


Instead of using a setTimeout, which can fail if you have a very slow device, you can use a counter to see how many callbacks still need to be called. If the counter reaches zero, you're all done :)

This is the recursive code:

var fileSync = new function(){
    this.filesystem = null;

    this.getFileSystem = function(callback){
        var rfs = window.requestFileSystem || window.webkitRequestFileSystem;

        rfs(
              1// '1' means PERSISTENT
            , 0// '0' is about max. storage size: 0==we don't know yet
            , function(filesystem){
                fileSync.filesystem = filesystem;
                callback(filesystem);
            }
            , function(e){
                alert('An error occured while requesting the fileSystem:\n\n'+ e.message);
            }
        );
    }

    this.readFilesFromReader = function(reader, callback, recurse, recurseFinishedCallback, recurseCounter)
    {
        if (recurse && !recurseCounter)
            recurseCounter = [1];

        reader.readEntries(function(res){
                callback(res);

                if (recurse)
                {
                    for (var i=0; i<res.length; i++) {
                        /* only handle directories */
                        if (res[i].isDirectory == true)
                        {
                            recurseCounter[0]++;
                            fileSync.readFilesFromReader(res[i].createReader(), callback, recurse, recurseFinishedCallback, recurseCounter);
                        }
                    }
                }
                /* W3C specs say: Continue calling readEntries() until an empty array is returned.
                 * You have to do this because the API might not return all entries in a single call.
                 * But... Phonegap doesn't seem to accommodate this, and instead always returns the same dir-entries... OMG, an infinite loop is created :-/
                */
                //if (res.length)
                //  fileSync.readFilesFromReader(reader, callback, recurse, recurseFinishedCallback, recurseCounter);
                //else
                if (recurse && --recurseCounter[0] == 0)
                {
                    recurseFinishedCallback();
                }
            }
        , function(e){
            fileSync.onError(e);
            if (recurse && --recurseCounter[0] == 0)
                recurseFinishedCallback();
        });
    };

    this.onError = function(e){
        utils.log('onError in fileSync: ' + JSON.stringify(e));
        if (utils.isDebugEnvironment())
            alert('onError in fileSync: '+JSON.stringify(e));
    }
}

var utils = new function(){
    this.log = function(){
        for (var i=0;i<arguments.length;i++)
            console.log(arguments[i]);
    }
    this.isDebugEnvironment = function(){ return true }// simplified
}

Example code to test this:

var myFiles = [];
var directoryCount = 0;

window.onerror = function(){ alert('window.onerror=\n\n' + arguments.join('\n')) }

var gotFilesCallback = function(entries)
{
    for (var i=0;i<entries.length;i++)
    {
        if (entries[i].isFile == true)
            myFiles.push(entries[i].fullPath)
        else
            ++directoryCount;
    }
}

var allDoneCallback = function(){
    alert('All files and directories were read.\nWe found '+myFiles.length+' files and '+directoryCount+' directories, shown on-screen now...');
    var div = document.createElement('div');
    div.innerHTML = '<div style="border: 1px solid red; position: absolute;top:10px;left:10%;width:80%; background: #eee;">'
        + '<b>Filesystem root:</b><i>' + fileSync.filesystem.root.fullPath + '</i><br><br>'
        + myFiles.join('<br>').split(fileSync.filesystem.root.fullPath).join('')
    + '</div>';
    document.body.appendChild(div);
}

/* on-device-ready / on-load, get the filesystem, and start reading files */
var docReadyEvent = window.cordova ? 'deviceready':'load';
document.addEventListener(docReadyEvent, function()
{
    fileSync.getFileSystem(function(filesystem){
        var rootDirReader = filesystem.root.createReader();
        fileSync.readFilesFromReader(rootDirReader, gotFilesCallback, true, allDoneCallback);
    })
}, false);


来源:https://stackoverflow.com/questions/11234933/how-to-iterate-through-file-system-directories-and-files-using-javascript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!