Progressive Web App 存取本机档案: File System Access API (14)

什麽是 File System Access API?

透过这个 File System Access API 就能透过程序操作本机上的档案,举例来说像是开启或储存档案等,部分浏览器会需要给予相关的权限才能进行操作,除了开启档案 API 本身也提供了开启目录并列举档案列表的功能。

对 Progressive Web App 也会在某些情境需要可以存取和操作本机端的档案,Google 的 Lab 提供了底下这个编辑器可以简单试玩:

https://googlechromelabs.github.io/text-editor/

browser-fs-access library:
https://github.com/GoogleChromeLabs/browser-fs-access

小编在几年前刚学习 SPA 时也写了个单页笔记的应用,能够快速的写一些笔记,也附上原始码和 Demo 连结。

原始码: https://github.com/LinYenCheng/vue-note
Demo 连结: https://linyencheng.github.io/vue-note/

透过浏览器读取本机档案

前几年小编其实没有使用过 File System Access API,所以显然在操作上其实有两种方式,那第一种是透过原生的 input 然後指定 typefile 就可以达到读取的效果,第二种是透过 File System Access API 的 showOpenFilePicker()

  1. 直接在 html 中加入 <input type="file" id="fileInput"> 并且简单撰写相关事件就能够使用,程序码如下:

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

fileInput.addEventListener('change', function(e) {
    var file = fileInput.files[0];
    var textType = /text.*/;

    if (file.type.match(textType)) {
        var reader = new FileReader();

        reader.onload = function(e) {
            maindata.gridData = [];
            var objText = JSON.parse(reader.result.toString());
            objText.gridData.forEach(function(element, index) {
                maindata.gridData.push(element);
            });
            console.log(objText);
        };

        reader.readAsText(file);
    } else {
        fileDisplayArea.innerText = "File not supported!";
    }
});
  1. 使用 File System Access API 中的 showOpenFilePicker(),最後取得的 File object 包含会一个 blob 可以透过以下的方法去取得相关的值:
  • slice()
  • stream()
  • text()
  • arrayBuffer()
let fileHandle;
butOpenFile.addEventListener('click', async () => {
  [fileHandle] = await window.showOpenFilePicker({
    // 可以提供预设
    startIn: 'pictures'
  });
  const file = await fileHandle.getFile();
  const contents = await file.text();
  textArea.value = contents;
});

showOpenFilePicker 中的 startIn 提供的预设值是常见的几个资料夹,能够提供指定

  • desktop
  • documents
  • downloads
  • music
  • pictures
  • videos

透过浏览器写入本机档案

操作上一样会有两种方式,第一种是产生出一个下载连结,将 Blob 档案透过下载的方式写入,第二种是透过 File System Access API 的 showSaveFilePicker()

  1. 将需要写入的内容先产生成 Blob 然後产生成下载连结,最後透过程序去点击连结去触发开启选择写入资料夹的视窗。

function saveFile(text) {
    var url = null;
    var data = new Blob([text], {
        type: 'text/plain;'
    });
    // If we are replacing a previously generated file we need to
    // manually revoke the object URL to avoid memory leaks.
    if (url !== null) {
        window.URL.revokeObjectURL(url);
    }

    url = window.URL.createObjectURL(data);

    var link = document.getElementById('exportText');
    link.href = url;
    var filename = window.prompt("输入档名") || 'export';
    link.download = filename + '.txt';
    link.click();
}

  1. 透过 showSaveFilePicker() 开启选择写入资料夹的视窗。

const fileHandle = await self.showSaveFilePicker({
  suggestedName: 'Untitled Text.txt',
  types: [{
    description: 'Text documents',
    accept: {
      'text/plain': ['.txt'],
    },
  }],
});

async function writeFile(fileHandle, contents) {
  // Create a FileSystemWritableFileStream to write to.
  const writable = await fileHandle.createWritable();
  // Write the contents of the file to the stream.
  await writable.write(contents);
  // Close the file and write the contents to disk.
  await writable.close();
}

透过浏览器删除本机档案

这个之前小编就没有试过其他方式,那透过 File System Access API 的 removeEntry() 是可以做到删除档案和删除资料夹所有内容的效果。

// 删除档案
await directoryHandle.removeEntry('Abandoned Projects.txt');
// 删除资料夹所有内容
await directoryHandle.removeEntry('Old Stuff', { recursive: true });

透过浏览器开启目录

File System Access API 的 showDirectoryPicker() 提供了我们操作目录并列举内容的功能。

const dirHandle = await window.showDirectoryPicker();
for await (const entry of dirHandle.values()) {
 console.log(entry.kind, entry.name);
}

File System Access API 权限

部分浏览器环境还是会有权限的问题,所以侦测权限相关就变成也是要写在程序码里,逻辑如下:


async function verifyPermission(fileHandle, readWrite) {
  const options = {};
  if (readWrite) {
    options.mode = 'readwrite';
  }
  // 看是否已同意
  if ((await fileHandle.queryPermission(options)) === 'granted') {
    return true;
  }
  // 看这次有没有要成功
  if ((await fileHandle.requestPermission(options)) === 'granted') {
    return true;
  }
  // 被拒绝
  return false;
}


<<:  # Day12--我们用协定说好要这样做了,你一定得OK!

>>:  Day12-TypeScript(TS)的选择性属性(Optional Properties)

没想太多就用了 MongoDB 的结果 (中)

初始设定 一开始我们就在 Azure 租一个 node , 程序後端(以下称API)、Nginx、M...

Unity自主学习(二):如何安装Unity(一)

昨天我们简单了解了Unity是什麽东西,而为了接下来的学习,便先从下载安装Unity引擎开始。 我们...

安全工程101

系统工程是一门应用知识来创建或获取一个系统的学科,该系统由相互关联的元素组成,这些元素在整个系统开...

[Day 16 - 小试身手] 用HTML、CSS、JS打造个人网站 (3)

在上一篇:用HTML、CSS、JS打造个人网站 (2),完成了网页的所有内容,接下来的工作就是让网...

电子书阅读器上的浏览器 [Day20] 翻译功能 (II) 取得网页全文

接着来讲讲怎麽取得 browser 目前网页中的本文内容,然後再把它转给昨天介绍字典 App。 取得...