Paso 6: Exporta todos al sistema de archivos

En este paso, aprenderás lo siguiente:

  • Cómo obtener una referencia a un archivo en el sistema de archivos externo
  • Cómo escribir en el sistema de archivos

Tiempo estimado para completar este paso: 20 minutos
Para obtener una vista previa de lo que completarás en este paso, ve hacia la parte inferior de esta página ↓.

Exportar tareas pendientes

En este paso, se agrega un botón de exportación a la app. Cuando se hace clic en él, los elementos pendientes actuales se guardan en un archivo de texto seleccionado por el usuario. Si el archivo existe, se reemplaza. De lo contrario, se creará un archivo nuevo.

Actualizar permisos

Los permisos del sistema de archivos se pueden solicitar como una string para acceso de solo lectura o un objeto con propiedades adicionales. Por ejemplo:

// Read only
"permissions": ["fileSystem"]

// Read and write
"permissions": [{"fileSystem": ["write"]}]

// Read, write, autocomplate previous input, and select folder directories instead of files
"permissions": [{"fileSystem": ["write", "retainEntries", "directory"]}]

Necesitas acceso de lectura y escritura. En manifest.json, solicita el permiso {fileSystem: [ "write" ] }:

"permissions": [
  "storage", 
  "alarms", 
  "notifications", 
  "webview",
  "<all_urls>", 
  { "fileSystem": ["write"] } 
],

Cómo actualizar la vista HTML

En index.html, agrega el botón Exportar al disco y una div donde la app muestre un mensaje de estado:

<footer id="info">
  <button id="toggleAlarm">Activate alarm</button>
  <button id="exportToDisk">Export to disk</button>
  <div id="status"></div>
  ...
</footer>

También en index.html, carga la secuencia de comandos export.js:

...
<script src="js/alarms.js"></script>
<script src="js/export.js"></script>

Crear secuencia de comandos de exportación

Crea un nuevo archivo JavaScript llamado export.js con el siguiente código. Guárdalo en la carpeta js.

(function() {

  var dbName = 'todos-vanillajs';

  var savedFileEntry, fileDisplayPath;

  function getTodosAsText(callback) {
  }

  function exportToFileEntry(fileEntry) {
  }

  function doExportToDisk() {
  }

  document.getElementById('exportToDisk').addEventListener('click', doExportToDisk);

})();

En este momento, export.js solo contiene un objeto de escucha de clics en el botón Exportar al disco y stubs para getTodosAsText(), exportToFileEntry y doExportToDisk().

Obtener elementos de tareas pendientes como texto

Actualiza getTodosAsText() para que lea todas las tareas de chrome.storage.local y genere una representación textual de ellas:

function getTodosAsText(callback) {
  chrome.storage.local.get(dbName, function(storedData) {
    var text = '';

    if ( storedData[dbName].todos ) {
      storedData[dbName].todos.forEach(function(todo) {
          text += '- ';
          if ( todo.completed ) {
            text += '[DONE] ';
          }
          text += todo.title;
          text += '\n';
        }, '');
    }

    callback(text);

  }.bind(this));
}

Elige un archivo

Actualiza doExportToDisk() con chrome.fileSystem.chooseEntry() para permitir que el usuario elija un archivo:

function doExportToDisk() {

  if (savedFileEntry) {

    exportToFileEntry(savedFileEntry);

  } else {

    chrome.fileSystem.chooseEntry( {
      type: 'saveFile',
      suggestedName: 'todos.txt',
      accepts: [ { description: 'Text files (*.txt)',
                   extensions: ['txt']} ],
      acceptsAllTypes: true
    }, exportToFileEntry);

  }
}

El primer parámetro de chrome.fileSystem.chooseEntry() es un objeto de opciones. El segundo parámetro es un método de devolución de llamada.

Si ya tienes un FileEntry guardado, úsalo cuando llames a exportToFileEntry(). Las referencias a archivos existen durante la vida útil del objeto que representa FileEntry. En este ejemplo, se vincula FileEntry a la ventana de la app para que el código JavaScript pueda escribir en el archivo seleccionado sin interacción del usuario, siempre que la ventana de la app permanezca abierta.

Usa FileEntry para escribir elementos de todos en el disco

Actualiza exportToFileEntry() para guardar las tareas pendientes como texto a través de la API web de FileEntry:

function exportToFileEntry(fileEntry) {
  savedFileEntry = fileEntry;

  var status = document.getElementById('status');

  // Use this to get a file path appropriate for displaying
  chrome.fileSystem.getDisplayPath(fileEntry, function(path) {
    fileDisplayPath = path;
    status.innerText = 'Exporting to '+path;
  });

  getTodosAsText( function(contents) {

    fileEntry.createWriter(function(fileWriter) {

      var truncated = false;
      var blob = new Blob([contents]);

      fileWriter.onwriteend = function(e) {
        if (!truncated) {
          truncated = true;
          // You need to explicitly set the file size to truncate
          // any content that might have been there before
          this.truncate(blob.size);
          return;
        }
        status.innerText = 'Export to '+fileDisplayPath+' completed';
      };

      fileWriter.onerror = function(e) {
        status.innerText = 'Export failed: '+e.toString();
      };

      fileWriter.write(blob);

    });
  });
}

chrome.fileSystem.getDisplayPath() obtiene una ruta de acceso al archivo que se puede mostrar y que da como resultado el estado div.

Usa fileEntry.createWriter() para crear un objeto FileWriter. Luego, fileWriter.write() puede escribir un BLOB en el sistema de archivos. Usa fileWriter.onwriteend() y fileWriter.onerror() para actualizar el estado div.

Para obtener más información sobre FileEntry, lee Cómo explorar las APIs de FileSystem en HTML5Rocks o consulta el FileEntry docs en MDN.

Conserva objetos FileEntry

Avanzado: Los objetos FileEntry no se pueden conservar de forma indefinida. Tu app necesita pedirle al usuario que elija un archivo cada vez que se inicia. Si tu app se vio forzada a reiniciarse debido a una falla o una actualización en el tiempo de ejecución, restoreEntry() es una opción para restablecer un FileEntry.

Si lo deseas, puedes probar guardando el ID que muestra retainEntry() y restablecerlo cuando se reinicie la app. (Pista: Agrega un objeto de escucha al evento onRestarted en la página en segundo plano).

Inicia tu app de tareas pendientes terminada

Has terminado el paso 6. Vuelve a cargar la app y agrega algunas tareas pendientes. Haz clic en Exportar al disco para exportar los todos a un archivo .txt.

La app de Todo con la exportación de tareas pendientes

Más información

Para obtener información más detallada sobre algunas de las APIs presentadas en este paso, consulta lo siguiente:

¿Todo listo para continuar con el siguiente paso? Ve al Paso 7: Publica tu app »