Bas*_*asj 21 html javascript serialization
我知道,出于安全原因,Javascript无法在文件系统中写入数据.我经常读到,用Javascript本地保存数据的唯一方法是使用cookies或localStorage
.
但它是可能的,在非常特殊的情况下,当文件被访问本地(通过互联网而不是),将数据写入本地?(没有任何服务器语言,甚至根本没有任何服务器......只是本地浏览HTML文件)
... HTML文件被新内容覆盖了吗?(即按下SAVE时应更新本地 HTML文件).
感谢Javascript,当本地访问HTML页面时,这可以保存文件吗?
注意:我希望能够以静默方式保存,而不是建议下载/保存对话框,用户必须在其中选择下载位置,然后"确定要覆盖"等.
编辑:为什么这个问题?我希望能够做一个HTML/JS记事本,我可以在没有任何服务器的情况下在本地运行(没有apache,没有php).我需要能够轻松保存,而无需处理对话框"你想在哪里下载文件?".
Awe*_*s01 13
你可以使用Blob函数:
function save() {
var htmlContent = ["your-content-here"];
var bl = new Blob(htmlContent, {type: "text/html"});
var a = document.createElement("a");
a.href = URL.createObjectURL(bl);
a.download = "your-download-name-here.html";
a.hidden = true;
document.body.appendChild(a);
a.innerHTML = "something random - nobody will see this, it doesn't matter what you put here";
a.click();
}
Run Code Online (Sandbox Code Playgroud)
并且您的文件将保存.
有一个相对较新的非标准文件系统访问 API(不要与早期的文件和目录条目 API或文件系统 API混淆)。它似乎是在 2019/2020 年在 Chromium/Chrome 中引入的,并且在 Firefox 或 Safari 中不支持。
使用此API时,本地打开的页面可以打开/保存其他本地文件并在页面中使用文件的数据。它确实需要初始权限才能保存,但当用户位于页面上时,特定文件的后续保存会“静默”进行。用户还可以授予对特定目录的权限,随后对该目录的读取和写入不需要批准。用户关闭网页的所有选项卡并重新打开页面后,需要再次批准。
您可以在https://web.dev/file-system-access/阅读有关这个新 API 的更多信息。它旨在用于制作更强大的 Web 应用程序。
关于它有几点需要注意:
默认情况下,它需要安全上下文才能运行。在 https、localhost 或通过 file:// 运行它应该可以工作。
您可以使用DataTransferItem通过拖放文件来获取文件句柄.getAsFileSystemHandle
最初读取或保存文件需要用户批准,并且只能通过用户交互启动。此后,后续读取和保存不需要批准,直到再次打开该站点。
文件句柄可以保存在页面中(因此,如果您正在编辑本地文件'/path/to/file.txt'
并重新加载页面,它将能够引用该文件)。它们似乎无法被字符串化,因此通过 IndexedDB 之类的东西存储(有关更多信息,请参阅此答案)。使用存储的句柄进行读/写需要用户交互和用户批准。
以下是一些简单的例子。它们似乎不能在跨域 iframe 中运行,因此您可能需要将它们另存为 html 文件并在 Chrome/Chromium 中打开它们。
<body>
<div><button id="open">Open</button><button id="save">Save</button></div>
<textarea id="editor" rows=10 cols=40></textarea>
<script>
let openButton = document.getElementById('open');
let saveButton = document.getElementById('save');
let editor = document.getElementById('editor');
let fileHandle;
async function openFile() {
try {
[fileHandle] = await window.showOpenFilePicker();
await restoreFromFile(fileHandle);
} catch (e) {
// might be user canceled
}
}
async function restoreFromFile() {
let file = await fileHandle.getFile();
let text = await file.text();
editor.value = text;
}
async function saveFile() {
var saveValue = editor.value;
if (!fileHandle) {
try {
fileHandle = await window.showSaveFilePicker();
} catch (e) {
// might be user canceled
}
}
if (!fileHandle || !await verifyPermissions(fileHandle)) {
return;
}
let writableStream = await fileHandle.createWritable();
await writableStream.write(saveValue);
await writableStream.close();
}
async function verifyPermissions(handle) {
if (await handle.queryPermission({ mode: 'readwrite' }) === 'granted') {
return true;
}
if (await handle.requestPermission({ mode: 'readwrite' }) === 'granted') {
return true;
}
return false;
}
document.body.addEventListener('dragover', function (e) {
e.preventDefault();
});
document.body.addEventListener('drop', async function (e) {
e.preventDefault();
for (const item of e.dataTransfer.items) {
if (item.kind === 'file') {
let entry = await item.getAsFileSystemHandle();
if (entry.kind === 'file') {
fileHandle = entry;
restoreFromFile();
} else if (entry.kind === 'directory') {
// handle directory
}
}
}
});
openButton.addEventListener('click', openFile);
saveButton.addEventListener('click', saveFile);
</script>
</body>
Run Code Online (Sandbox Code Playgroud)
存储文件句柄可能很棘手,因为它们不能取消字符串化,尽管显然它们可以与 IndexedDB 一起使用,并且大多数与history.state
. 在本示例中,我们将使用idb-keyval访问 IndexedDB 来存储文件句柄。要查看其工作情况,请打开或保存文件,然后重新加载页面并按“恢复”按钮。此示例使用/sf/answers/4615723731/中的一些代码。
<body>
<script src="https://unpkg.com/idb-keyval@6.1.0/dist/umd.js"></script>
<div><button id="restore" style="display:none">Restore</button><button id="open">Open</button><button id="save">Save</button></div>
<textarea id="editor" rows=10 cols=40></textarea>
<script>
let restoreButton = document.getElementById('restore');
let openButton = document.getElementById('open');
let saveButton = document.getElementById('save');
let editor = document.getElementById('editor');
let fileHandle;
async function openFile() {
try {
[fileHandle] = await window.showOpenFilePicker();
await restoreFromFile(fileHandle);
} catch (e) {
// might be user canceled
}
}
async function restoreFromFile() {
let file = await fileHandle.getFile();
let text = await file.text();
await idbKeyval.set('file', fileHandle);
editor.value = text;
restoreButton.style.display = 'none';
}
async function saveFile() {
var saveValue = editor.value;
if (!fileHandle) {
try {
fileHandle = await window.showSaveFilePicker();
await idbKeyval.set('file', fileHandle);
} catch (e) {
// might be user canceled
}
}
if (!fileHandle || !await verifyPermissions(fileHandle)) {
return;
}
let writableStream = await fileHandle.createWritable();
await writableStream.write(saveValue);
await writableStream.close();
restoreButton.style.display = 'none';
}
async function verifyPermissions(handle) {
if (await handle.queryPermission({ mode: 'readwrite' }) === 'granted') {
return true;
}
if (await handle.requestPermission({ mode: 'readwrite' }) === 'granted') {
return true;
}
return false;
}
async function init() {
var previousFileHandle = await idbKeyval.get('file');
if (previousFileHandle) {
restoreButton.style.display = 'inline-block';
restoreButton.addEventListener('click', async function (e) {
if (await verifyPermissions(previousFileHandle)) {
fileHandle = previousFileHandle;
await restoreFromFile();
}
});
}
document.body.addEventListener('dragover', function (e) {
e.preventDefault();
});
document.body.addEventListener('drop', async function (e) {
e.preventDefault();
for (const item of e.dataTransfer.items) {
console.log(item);
if (item.kind === 'file') {
let entry = await item.getAsFileSystemHandle();
if (entry.kind === 'file') {
fileHandle = entry;
restoreFromFile();
} else if (entry.kind === 'directory') {
// handle directory
}
}
}
});
openButton.addEventListener('click', openFile);
saveButton.addEventListener('click', saveFile);
}
init();
</script>
</body>
Run Code Online (Sandbox Code Playgroud)
Firefox 和 Safari 支持似乎不太可能,至少在短期内是这样。请参阅https://github.com/mozilla/standards-positions/issues/154和https://lists.webkit.org/pipermail/webkit-dev/2020-August/031362.html
来自W3C File API标准的规范答案:
用户代理应提供暴露给脚本的API,以暴露上述功能.无论何时发生与文件系统的交互,UI都会通知用户,从而使用户能够完全取消或中止交易.用户会收到任何文件选择的通知,并可以取消这些选择.在没有用户干预的情况下,不会对这些API进行无提示调用.
基本上,由于安全设置,每次下载文件时,浏览器都会确保用户确实想要保存文件.浏览器并没有真正区分计算机上的JavaScript和Web服务器上的JavaScript.唯一的区别是浏览器如何访问文件,因此在本地存储页面不会产生任何影响.
解决方法:
但是,您可以将<div>
cookie 的innerHTML存储起来.当用户返回时,您可以从cookie中加载它.虽然它不是将文件保存到用户的计算机上,但它应该与覆盖文件具有相同的效果.当用户返回时,他们将看到他们上次输入的内容.缺点是,如果用户清除他们的网站数据,他们的信息将丢失.由于忽略用户清除本地存储的请求也是一个安全问题,因此实际上无法绕过它.
但是,您也可以执行以下操作:
使用cookie:您可以在本地页面上使用JavaScript cookie.只需将其放入文件并在浏览器中打开即可:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p id="timesVisited"></p>
<script type="text/javascript">
var timesVisited = parseInt(document.cookie.split("=")[1]);
if (isNaN(timesVisited)) timesVisited = 0;
timesVisited++;
document.cookie = "timesVisited=" + timesVisited;
document.getElementById("timesVisited").innerHTML = "You ran this snippet " + timesVisited + " times.";
</script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
52775 次 |
最近记录: |