6단계: 파일 시스템으로 할 일 내보내기

이 단계에서 학습할 내용은 다음과 같습니다.

  • 외부 파일 시스템의 파일에 대한 참조를 가져오는 방법
  • 파일 시스템에 쓰는 방법

이 단계를 완료하는 데 걸리는 예상 시간: 20분
이 단계에서 완료할 작업을 미리 보려면 이 페이지 하단으로 이동 ↓합니다.

할 일 내보내기

이 단계에서는 앱에 내보내기 버튼을 추가합니다. 클릭하면 현재 할 일 항목이 사용자가 선택한 텍스트 파일에 저장됩니다. 파일이 있으면 대체됩니다. 그렇지 않으면 새 파일이 생성됩니다.

권한 업데이트

파일 시스템 권한은 읽기 전용 액세스를 위한 문자열 또는 추가 속성이 있는 객체로 요청할 수 있습니다. 예를 들면 다음과 같습니다.

// 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"]}]

읽기 및 쓰기 액세스 권한이 필요합니다. manifest.json에서 {fileSystem: [ "write" ] } 권한을 요청합니다.

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

HTML 보기 업데이트

index.html에서 디스크로 내보내기 버튼과 앱이 상태 메시지를 표시하는 div를 추가합니다.

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

또한 index.html에서 export.js 스크립트를 로드합니다.

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

내보내기 스크립트 만들기

아래 코드를 사용하여 export.js라는 새 자바스크립트 파일을 만듭니다. 파일을 js 폴더에 저장합니다.

(function() {

  var dbName = 'todos-vanillajs';

  var savedFileEntry, fileDisplayPath;

  function getTodosAsText(callback) {
  }

  function exportToFileEntry(fileEntry) {
  }

  function doExportToDisk() {
  }

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

})();

현재 export.js에는 디스크로 내보내기 버튼의 클릭 리스너와 getTodosAsText(), exportToFileEntry, doExportToDisk()의 스텁만 포함되어 있습니다.

할 일 항목을 텍스트로 받기

chrome.storage.local에서 할 일을 읽고 이러한 작업의 텍스트 표현을 생성하도록 getTodosAsText()를 업데이트합니다.

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));
}

파일 선택

사용자가 파일을 선택할 수 있도록 doExportToDisk()chrome.fileSystem.chooseEntry()로 업데이트합니다.

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);

  }
}

chrome.fileSystem.chooseEntry()의 첫 번째 매개변수는 옵션 객체입니다. 두 번째 매개변수는 콜백 메서드입니다.

저장된 FileEntry가 이미 있다면 exportToFileEntry()를 호출할 때 대신 사용합니다. 파일 참조는 FileEntry를 나타내는 객체의 전체 기간 동안 존재합니다. 이 예에서는 앱 창이 열려 있는 한 사용자 상호작용 없이 자바스크립트 코드가 선택된 파일에 쓸 수 있도록 FileEntry를 앱 창에 연결합니다.

FileEntry를 사용하여 디스크에 할 일 항목 쓰기

FileEntry Web API를 통해 할 일을 텍스트로 저장하도록 exportToFileEntry()을 업데이트합니다.

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()div 상태로 출력하는 표시 가능한 파일 경로를 가져옵니다.

fileEntry.createWriter()를 사용하여 FileWriter 객체를 만듭니다. 그러면 fileWriter.write()Blob을 파일 시스템에 쓸 수 있습니다. fileWriter.onwriteend()fileWriter.onerror()를 사용하여 div 상태를 업데이트합니다.

FileEntry에 관한 자세한 내용은 HTML5Rocks에서 FileSystem API 탐색을 참고하거나 MDN에서 FileEntry docs를 참고하세요.

FileEntry 객체 유지

고급: FileEntry 객체는 무기한 지속될 수 없습니다. 앱은 실행될 때마다 사용자에게 파일을 선택하도록 요청해야 합니다. 런타임 비정상 종료나 업데이트로 인해 앱이 강제로 다시 시작된 경우 restoreEntry()를 사용하여 FileEntry를 복원할 수 있습니다.

원하는 경우 retainEntry()에서 반환된 ID를 저장하고 앱을 다시 시작할 때 복원하여 실험해 보세요. (힌트: 백그라운드 페이지의 onRestarted 이벤트에 리스너를 추가하세요.)

완성된 Todo 앱을 실행합니다.

6단계를 완료했습니다. 앱을 새로고침하고 할 일을 추가합니다. 디스크로 내보내기를 클릭하여 할 일을 .txt 파일로 내보냅니다.

내보낸 할 일이 있는 Todo 앱

추가 정보

이 단계에서 도입된 일부 API에 관한 자세한 내용은 다음을 참고하세요.

다음 단계로 진행할 준비가 되셨나요? 7단계 - 앱 게시 »로 이동합니다.