Удаление и чтение файлов Javascript — асинхронная рекурсия

Итак, я пытаюсь создать веб-приложение для файловой системы. Прямо сейчас пользователь может перетаскивать файлы на экран, и я могу их читать, включая все файлы в удаленном каталоге. Но я не знаю, когда скрипт закончит чтение файлов.

Некоторый код:

Эта первая функция обрабатывает событие 'drop' и будет перебирать каждый файл и отправлять его другой функции, которая будет читать его содержимое.

function readDrop( evt )
{
    for( var i = 0; i < evt.dataTransfer.files.length; i++)
    {
        var entry = evt.dataTransfer.items[i].webkitGetAsEntry();

        if(entry)
            readContents(entry, "");
    }

    //Do stuff after all files and directories have been read.
}

Эта функция является рекурсивным считывателем FileEntry. Если это файл, я буду читать файл FileEntry. Если это каталог, он будет перебирать содержимое и передавать его через эту функцию.

function readContents(entry, path)
{
    if( entry.isFile )
    {
        readFileData( entry, path, function(fileData)
        {
            _MyFiles.push( fileData );
        });
    }
    else if( entry.isDirectory )
    {
        var directoryReader = entry.createReader();
        var path = path + entry.name;

        directoryReader.readEntries(function(results)
        {
            for( var j = 0; j < results.length; j++ )
            {
                readContents(entry, path);
            }

        }, errorHandler)
    }
}

А вот моя функция для чтения файлов. Обратный вызов просто помещает объект fileData в глобальный массив.

function readFileData(entry, path, callback)
{
    var fileData = {"name": entry.name, "size": 0, "path": path, "file": entry};

    entry.file(function(file)
    {
        fileData["size"] = file.size;
        callback( fileData );
    }
}

Я не уверен, куда идти отсюда, чтобы у меня был обратный вызов, когда все файлы и каталоги были прочитаны.


person frshca    schedule 15.09.2013    source источник
comment
Самое интересное в коде, который вы пропустили, где у вас есть комментарий //read the file — покажите нам этот код, и мы сможем вам помочь.   -  person Martin Jespersen    schedule 15.09.2013
comment
Хорошо, я думаю, я не думал, что часть кода так важна, но я добавил ее   -  person frshca    schedule 15.09.2013


Ответы (2)


FileSystem API кажется не очень подходящим для задачи полного рекурсивного обхода, возможно, это одна из причин, по которой другие поставщики не используют его. В любом случае, с загадочной комбинацией Обещаний я думаю, что смог достичь этой цели:

    function traverse_directory(entry) {
        let reader = entry.createReader();
        // Resolved when the entire directory is traversed
        return new Promise((resolve_directory) => {
            var iteration_attempts = [];
            (function read_entries() {
                // According to the FileSystem API spec, readEntries() must be called until
                // it calls the callback with an empty array.  Seriously??
                reader.readEntries((entries) => {
                    if (!entries.length) {
                        // Done iterating this particular directory
                        resolve_directory(Promise.all(iteration_attempts));
                    } else {
                        // Add a list of promises for each directory entry.  If the entry is itself 
                        // a directory, then that promise won't resolve until it is fully traversed.
                        iteration_attempts.push(Promise.all(entries.map((entry) => {
                            if (entry.isFile) {
                                // DO SOMETHING WITH FILES
                                return entry;
                            } else {
                                // DO SOMETHING WITH DIRECTORIES
                                return traverse_directory(entry);
                            }
                        })));
                        // Try calling readEntries() again for the same dir, according to spec
                        read_entries();
                    }
                }, errorHandler );
            })();
        });
    }

    traverse_directory(my_directory_entry).then(()=> {
        // AT THIS POINT THE DIRECTORY SHOULD BE FULLY TRAVERSED.
    });
person drarmstr    schedule 05.01.2016
comment
ты современный герой - person Christophe Marois; 11.04.2016

Следуя ответу drarmstr, я изменил функцию, чтобы она соответствовала стандартам Airbnb ESLint, и хотел сделать некоторые дополнительные комментарии по ее использованию и результатам.

Вот новая функция:

function traverseDirectory(entry) {
  const reader = entry.createReader();
  // Resolved when the entire directory is traversed
  return new Promise((resolve, reject) => {
    const iterationAttempts = [];
    function readEntries() {
      // According to the FileSystem API spec, readEntries() must be called until
      // it calls the callback with an empty array.  Seriously??
      reader.readEntries((entries) => {
        if (!entries.length) {
          // Done iterating this particular directory
          resolve(Promise.all(iterationAttempts));
        } else {
          // Add a list of promises for each directory entry.  If the entry is itself
          // a directory, then that promise won't resolve until it is fully traversed.
          iterationAttempts.push(Promise.all(entries.map((ientry) => {
            if (ientry.isFile) {
              // DO SOMETHING WITH FILES
              return ientry;
            }
            // DO SOMETHING WITH DIRECTORIES
            return traverseDirectory(ientry);
          })));
          // Try calling readEntries() again for the same dir, according to spec
          readEntries();
        }
      }, error => reject(error));
    }
    readEntries();
  });
}

вот обработчик события drop:

function dropHandler(evt) {
  evt.preventDefault();
  const data = evt.dataTransfer.items;
  for (let i = 0; i < data.length; i += 1) {
    const item = data[i];
    const entry = item.webkitGetAsEntry();
    traverseDirectory(entry).then(result => console.log(result));
  }
}

Переменная result в конце содержит массив, отражающий древовидную структуру папки, которую вы перетащили.

Например, вот репозиторий git для моего собственного сайта, пропущенный через приведенный выше код:

введите здесь описание изображения

Вот репозиторий Git для сравнения: https://github.com/tomjn/tomjn.com

person Tom J Nowell    schedule 25.04.2018