背景:
最近发现drawio的一些功能很黑魔法。
比如首次创建一个文件,浏览器系统弹窗保存到本地,(到这为止都是常规保存文件的套路,很好实现)。
接下来,你在浏览器中继续绘制编辑你创建的图表,ctrol+S 保存之后,神奇的事情发生了,没有任何系统弹窗,最新的内容已经被静默地写入了首次创建的文件中。
这个行为是有点反直觉的,相当于在无感知的情况下,直接读写系统文件。浏览器一般不会给这么大的权限。
如何实现
如果有,也只能是浏览器提供的能力,于是,找到了这个 API: FileSystemWritableFileStream
https://developer.mozilla.org/en-US/docs/Web/API/FileSystemWritableFileStream
兼容性很不好:

思路:
- 首次保存创造了一个FileSystemFileHandle对象:newHandle,提供对该读写文件的能力。
- 更新文件内容的时候,通过FileSystemFileHandle创造一个对该文件写入流 :writableStream。
- 使用writableStream.write 写入最新的文本Blob,(注意此处是覆盖式的)。
- 关闭流
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>file save</title> <style> #addText { width: 400px; height: 200px; } </style> </head> <body> <h1>保存为本地文件并持续实时保存最新编辑的内容</h1> <h4>text to save:</h4> <div> <textarea id="addText" name="addText">hello</textarea> </div> <button onclick="saveFile()">1. start save</button> <button onclick="updateFile()">2. update</button> <h4>file contents:</h4> <p id="fileContent"></p> <script> let $textToAdd; let $fileContent; let newHandle;
const pickerOpts = { types: [ { description: "Text file", accept: { "text/plain": [".txt"], }, }, ], suggestedName: "testFile", excludeAcceptAllOption: true, multiple: false, };
async function saveFile() { console.log("showSaveFilePicker"); newHandle = await window.showSaveFilePicker(pickerOpts); console.log("createWritable"); const writableStream = await newHandle.createWritable();
updateFile(); }
async function getFileContents() { const fileData = await newHandle.getFile(); const res = await fileData.text(); console.log("fileText: ", res); $fileContent.innerText = res; }
async function updateFileContent(text) { const writableStream = await newHandle.createWritable();
const data = new Blob([text], { type: "text" }); await writableStream.write(data);
await writableStream.close(); }
async function updateFile() { const text = $textToAdd.value; await updateFileContent(text); await getFileContents(); }
document.addEventListener("DOMContentLoaded", () => { $textToAdd = document.getElementById("addText"); $fileContent = document.getElementById("fileContent"); }); </script> </body> </html>
|
发散一下:可以利用这个特性进行攻击吗?
比如前端通过js改写下载文件的内容。
可以继续研究一下这个API有什么安全限制。
https://juejin.cn/post/7086054628294557733