Based on http://html5-demos.appspot.com/static/a.download.html: (archived)
var fileContent = "My epic novel that I don't want to lose.";
var bb = new Blob([fileContent ], { type: 'text/plain' });
var a = document.createElement('a');
a.download = 'download.txt';
a.href = window.URL.createObjectURL(bb);
a.click();
Modified the original fiddle: http://jsfiddle.net/9av2mfjx/
Answer from Stanislav on Stack OverflowBased on http://html5-demos.appspot.com/static/a.download.html: (archived)
var fileContent = "My epic novel that I don't want to lose.";
var bb = new Blob([fileContent ], { type: 'text/plain' });
var a = document.createElement('a');
a.download = 'download.txt';
a.href = window.URL.createObjectURL(bb);
a.click();
Modified the original fiddle: http://jsfiddle.net/9av2mfjx/
You should check the download attribute and the window.URL method because the download attribute doesn't seem to like data URI.
This example by Google is pretty much what you are trying to do.
html - How to save string from input to existing text JAVASCRIPT - Stack Overflow
javascript - save restore local storage to a local file - Stack Overflow
javascript - Save data to local storage - Stack Overflow
javascript - How to store files in local storage - Stack Overflow
You can create files in browser using Blob and URL.createObjectURL. All recent browsers support this.
You can not directly save the file you create, since that would cause massive security problems, but you can provide it as a download link for the user. You can suggest a file name via the download attribute of the link, in browsers that support the download attribute. As with any other download, the user downloading the file will have the final say on the file name though.
var textFile = null,
makeTextFile = function (text) {
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 (textFile !== null) {
window.URL.revokeObjectURL(textFile);
}
textFile = window.URL.createObjectURL(data);
// returns a URL you can use as a href
return textFile;
};
Here's an example that uses this technique to save arbitrary text from a textarea.
If you want to immediately initiate the download instead of requiring the user to click on a link, you can use mouse events to simulate a mouse click on the link as Lifecube's answer did. I've created an updated example that uses this technique.
var create = document.getElementById('create'),
textbox = document.getElementById('textbox');
create.addEventListener('click', function () {
var link = document.createElement('a');
link.setAttribute('download', 'info.txt');
link.href = makeTextFile(textbox.value);
document.body.appendChild(link);
// wait for the link to be added to the document
window.requestAnimationFrame(function () {
var event = new MouseEvent('click');
link.dispatchEvent(event);
document.body.removeChild(link);
});
}, false);
Some suggestions for this -
- If you are trying to write a file on client machine, You can't do this in any cross-browser way. IE does have methods to enable "trusted" applications to use ActiveX objects to read/write file.
- If you are trying to save it on your server then simply pass on the text data to your server and execute the file writing code using some server side language.
- To store some information on the client side that is considerably small, you can go for cookies.
- Using the HTML5 API for Local Storage.
I probably would have just tacked this on as a comment to Nathaniel Johnson's answer, but I don't have the reputation yet! With regard with those methods, here are some more simple versions of his functions:
function getLocalStorage() {
return JSON.stringify(localStorage)
}
function writeLocalStorage(data) {
Object.keys(data).forEach(function(key) { localStorage.setItem(key, data[key])})
}
The process for saving and retrieving local storage has two parts.
First you must be able to retrieve the contents of local storage in a form that is manageable in javascript. Since local storage is a map of key-value pairs the easiest way to this is to turn local storage into a javascript object. Then take this object and turn it into a JSON string. What you do with this string is up to you but I find it easiest to just have the user copy the string into an email.
function getLocalStorage() {
var a = {};
for (var i = 0; i < localStorage.length; i++) {
var k = localStorage.key(i);
var v = localStorage.getItem(k);
a[k] = v;
}
var s = JSON.stringify(a);
return s;
}
When I get the string, I use the following function to turn my local storage into a copy of their local storage. Remember to wipe your local storage clean before duplicating their data with a call to localStorage.clear()
function writeLocalStorage(data) {
var o = JSON.parse(data);
for (var property in o) {
if (o.hasOwnProperty(property)) {
localStorage.setItem(property, o[property]);
}
}
}
The last part of your question is how to protect the data from overwriting. You can't write to a local file, however, you can have copy the data into <textarea> and tell the user how to copy and paste the data into a email or a more direct approach.
You can use localStorage like this:
// Retrieve your data from locaStorage
var saveData = JSON.parse(localStorage.saveData || null) || {};
// Store your data.
function saveStuff(obj) {
saveData.obj = obj;
// saveData.foo = foo;
saveData.time = new Date().getTime();
localStorage.saveData = JSON.stringify(saveData);
}
// Do something with your data.
function loadStuff() {
return saveData.obj || "default";
}
Update 2022:
Since people still view this here's a more modern version. I like to wrap things like this in a service which gives you more control and type safety. You can easily add things like validation and errorhandling.
class LocalStorageService {
#keys = {
persons: 'persons',
};
constructor() {
this.storage = window.localStorage;
}
addPerson(person) {
const persons = this.getPersons();
persons.push(person);
this.setPersons(persons);
}
getPersons() {
return JSON.parse(this.storage.getItem(this.#keys.persons)) || [];
}
getPerson(id) {
const persons = this.getPersons();
return persons.find((person) => person.id === id);
}
setPersons(persons) {
this.storage.setItem(this.#keys.persons, JSON.stringify(persons));
}
removePerson(person) {
const persons = this.getPersons();
const index = persons.indexOf(person);
persons.splice(index, 1);
this.setPersons(persons);
}
clear() {
this.storage.clear();
}
}
const storageService = new LocalStorageService();
const person = {
id: '1',
name: 'John',
age: '20',
};
storageService.addPerson(person);
const persons = storageService.getPersons();
console.log(persons);
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Web Storage API - MDN
First, you should use console instead of alert.
And, if you want to use the retrieved object somewhere, you'd better store it in a variable:
var testObject = { 'one': 1, 'two': 2, 'three': 3 };
localStorage.setItem('testObject', JSON.stringify(testObject));
var retrievedObject = JSON.parse(localStorage.getItem('testObject'));
console.log('retrievedObject: ', retrievedObject);
Node.js:
var fs = require('fs');
fs.writeFile("test.txt", jsonData, function(err) {
if (err) {
console.log(err);
}
});
Browser (webapi):
function download(content, fileName, contentType) {
var a = document.createElement("a");
var file = new Blob([content], {type: contentType});
a.href = URL.createObjectURL(file);
a.download = fileName;
a.click();
}
download(jsonData, 'json.txt', 'text/plain');
It's my solution to save local data to txt file.
function export2txt() {
const originalData = {
members: [{
name: "cliff",
age: "34"
},
{
name: "ted",
age: "42"
},
{
name: "bob",
age: "12"
}
]
};
const a = document.createElement("a");
a.href = URL.createObjectURL(new Blob([JSON.stringify(originalData, null, 2)], {
type: "text/plain"
}));
a.setAttribute("download", "data.txt");
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
<button onclick="export2txt()">Export data to local txt file</button>
You can just use the Blob function:
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();
}
and your file will save.
Chromium's File System Access API (introduced in 2019)
There's a relatively new, non-standard File System Access API (not to be confused with the earlier File and Directory Entries API or the File System API). It looks like it was introduced in 2019/2020 in Chromium/Chrome, and doesn't have support in Firefox or Safari.
When using this API, a locally opened page can open/save other local files and use the files' data in the page. It does require initial permission to save, but while the user is on the page, subsequent saves of specific files do so 'silently'. A user can also grant permission to a specific directory, in which subsequent reads and writes to that directory don't require approval. Approval is needed again after the user closes all the tabs to the web page and reopens the page.
You can read more about this newish API at https://web.dev/file-system-access/. It's meant to be used to make more powerful web applications.
A few things to note about it:
By default, it requires a secure context to run. Running it on https, localhost, or through file:// should work.
You can get a file handle from dragging and dropping a file by using DataTransferItem
.getAsFileSystemHandleInitially reading or saving a file requires user approval and can only be initiated via a user interaction. After that, subsequent reads and saves don't need approval, until the site is opened again.

Handles to files can be saved in the page (so if you were editing local file
'/path/to/file.txt', and reload the page, it would be able to have a reference to the file). They can't seemingly be stringified, so are stored through something like IndexedDB (see this answer for more info). Using stored handles to read/write requires user interaction and user approval.
Here are some simple examples. They don't seem to run in a cross-domain iframe, so you probably need to save them as an html file and open them up in Chrome/Chromium.
Opening and Saving, with Drag and Drop (no external libraries):
<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>
Storing and Retrieving a File Handle using idb-keyval:
Storing file handles can be tricky, since they can't be unstringified, though apparently they can be used with IndexedDB and mostly with history.state. For this example we'll use idb-keyval to access IndexedDB to store a file handle. To see it work, open or save a file, and then reload the page and press the 'Restore' button. This example uses some code from https://stackoverflow.com/a/65938910/.
<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>
Additional Notes
Firefox and Safari support seems to be unlikely, at least in the near term. See https://github.com/mozilla/standards-positions/issues/154 and https://lists.webkit.org/pipermail/webkit-dev/2020-August/031362.html
You cannot serialize file API object.
Not that it helps with the specific problem, but ... Although I haven't used this, if you look at the article it seems that there are ways (although not supported yet by most browsers) to store the offline image data to some files so as to restore them afterward when the user is online (and not to use localStorage)
Convert it to base64 and then save it.
function gotPhoto(element) {
var file = element.files[0];
var reader = new FileReader()
reader.onload = function(base64) {
localStorage["file"] = base64;
}
reader.readAsDataURL(file);
}
// Saved to localstorage
function getPhoto() {
var base64 = localStorage["file"];
var base64Parts = base64.split(",");
var fileFormat = base64Parts[0].split(";")[1];
var fileContent = base64Parts[1];
var file = new File([fileContent], "file name here", {type: fileFormat});
return file;
}
// Retreived file object
I want to save some numbers from a small program in a local JSON file.
Example:
const increaseButton = document.getElementById("increase-button")
const numberField = document.getElementById("count-field")
let myNum = 0
let myArray = []
increaseButton.addEventListener("click", increaseNum)
function increaseNum() {
myNum++
numberField.innerText = myNum
myArray.pop()
myArray.push(myNum)
let jsonString = JSON.stringify(Object.assign({}, myArray))
console.log(jsonString)
}
The jsonString should be saved in a local file "myjson.json".
Goal: If I increase the number with the increaseButton, the new value should be saved in the "myjson.json" file:
{
"numbers": {
"0":1}
}The only option I found was with node.js plugin "fs" and a lot of code. Couldn't it be easier?
You cannot generate a file using javascript.
What you can do however, is do it with a hidden iframe that links to a PHP file. That PHP file should contain the proper headers (Content-Type, Content-Disposition, etc).
You should find more details here: Download file through an ajax call php
You can use the 'download' and 'data' attribute like this,
var div = document.getElementById('div');
var a = document.createElement('a');
div.appendChild(a);
a.text='download test file';
a.download='aaaabbb.txt';
a.href="data:text/plain," + 'aaaaaaaaaccccccccccccc';
<div id="div">aaaaa</div>