- There are a few major storage APIs
- Web Storage
- Indexed Database
- Web SQL Database – This has been deprecated and will no longer be developed or supported in the future.
这已经被弃用了,将来也不会再开发或支持了。 - File Access
- https://developer.mozilla.org/en-US/docs/Web/API/File
- https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications
# Web Storage
Simple and fairly widely supported way to store data in the client browser.
在客户端浏览器中存储数据的简单且广受支持的方法。Simple string only Key/Value storage
Key/Value 存储
IMPORTANT if you want to store a JavaScript object you must useJSON.stringify()
andJSON.parse()
.
重要的是,如果想存储一个 JavaScript 对象,必须使用JSON.stringify()
和JSON.parse()
。Different than cookies as this is a JavaScript API
与 cookie 不同,这是一个 JavaScript APIThere two areas you can store data in
有两个区域可以存储数据localStorage
– persistant storage after browser is closed
浏览器关闭后的持久化存储sessionStorage
– only stores for current browser session
仅存储当前浏览器会话
Data is saved per origin and there is a data limit
每个来源都保存数据,并且有数据限制
Firefox is around 10MBhttps://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API
https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
The storage object is exposed on the
window
object
存储对象暴露在window
对象上window.localStorage
or justlocalStorage
You can access key/value pairs in multiple ways
localStorage.colorSetting = '#a4509b'; | |
localStorage['colorSetting'] = '#a4509b'; | |
localStorage.setItem('colorSetting', '#a4509b'); | |
localStorage.getItem('colorSetting'); | |
localStorage.removeItem('colorSetting'); |
- There is also a storage event that fires that you can listen for and react to
还有一个触发的存储事件,您可以监听并对其作出反应
# Indexed DB
IndexedDB is a low-level API for client-side storage of significant amounts of structured data, including files/blobs, which also enables high performance searches of this data using indexes.
IndexedDB 是一个用于客户端存储大量结构化数据 (包括文件 /blob) 的底层 API,它还支持使用索引对这些数据进行高性能搜索。Transactional based database system, non-sql based, JavaScript-based object-oriented database.
基于事务的数据库系统,非 sql 的,基于 javascript 的面向对象数据库。Store and retrieve objects based on a key.
基于键存储和检索对象。Asynchronous API that uses a lot of function callbacks
使用大量函数回调的异步 APIStorage limits do exist but much larger than Web Storage and differ by browser
存储限制确实存在,但比 Web 存储大得多,而且不同的浏览器也不同https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Browser_storage_limits_and_eviction_criteria
https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
The basic pattern that Indexed DB encourages is the following:
- Open a database.
- Create an object store in the database.
- Start a transaction and make a request to do some database operation, like adding or retrieving data.
启动事务并请求执行一些数据库操作,如添加或检索数据。 - Wait for the operation to complete by listening to the right kind of DOM event.
通过侦听正确类型的 DOM 事件,等待操作完成。 - Do something with the results (which can be found on the request object).
对结果执行一些操作 (可以在请求对象中找到)。
https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
http://code.tutsplus.com/tutorials/working-with-indexeddb--net-34673
# DEMO
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | |
<title>Index DB Note Demo</title> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"> | |
<link rel="stylesheet" href="idb-demo.css"> | |
</head> | |
<body> | |
<div class="container"> | |
<div class="jumbotron"> | |
<h1>IndexDB Note Example</h1> | |
<p>A simple example for CRUD with IndexDB</p> | |
<button id="addbtn" class="btn btn-primary btn-lg">Add Note</button> | |
</div> | |
<div id="note-grid"> | |
<div class="note"> | |
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse cursus mi in ipsum cursus congue. Morbi molestie lorem nisl, vel tristique lorem pulvinar sed. Phasellus laoreet cursus felis, sollicitudin faucibus nisi lacinia non. Aliquam erat volutpat. Sed finibus sodales urna sit amet rutrum. Nullam accumsan est a porttitor viverra. Etiam pretium eros ipsum, non aliquet elit scelerisque ut. Nulla vulputate turpis eget libero pulvinar, aliquam mollis nibh interdum. Cras et nunc vel odio ornare condimentum in nec mauris. Suspendisse non magna ligula. Pellentesque sollicitudin massa ac elit elementum, quis volutpat justo elementum.</p> | |
<div class="note-actions"> | |
<button class="edit-note btn btn-sm btn-secondary">edit</button> | |
<button class="delete-note btn btn-sm btn-secondary">delete</button> | |
</div> | |
</div> | |
<div class="note"> | |
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse cursus mi in ipsum cursus congue. Morbi molestie lorem nisl, vel tristique lorem pulvinar sed. Phasellus laoreet cursus felis, sollicitudin faucibus nisi lacinia non. Aliquam erat volutpat. Sed finibus sodales urna sit amet rutrum. Nullam accumsan est a porttitor viverra. Etiam pretium eros ipsum, non aliquet elit scelerisque ut. Nulla vulputate turpis eget libero pulvinar, aliquam mollis nibh interdum. Cras et nunc vel odio ornare condimentum in nec mauris. Suspendisse non magna ligula. Pellentesque sollicitudin massa ac elit elementum, quis volutpat justo elementum.</p> | |
<div class="note-actions"> | |
<button class="edit-note btn btn-sm btn-secondary">edit</button> | |
<button class="delete-note btn btn-sm btn-secondary">delete</button> | |
</div> | |
</div> | |
<div class="note"> | |
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse cursus mi in ipsum cursus congue. Morbi molestie lorem nisl, vel tristique lorem pulvinar sed. Phasellus laoreet cursus felis, sollicitudin faucibus nisi lacinia non. Aliquam erat volutpat. Sed finibus sodales urna sit amet rutrum. Nullam accumsan est a porttitor viverra. Etiam pretium eros ipsum, non aliquet elit scelerisque ut. Nulla vulputate turpis eget libero pulvinar, aliquam mollis nibh interdum. Cras et nunc vel odio ornare condimentum in nec mauris. Suspendisse non magna ligula. Pellentesque sollicitudin massa ac elit elementum, quis volutpat justo elementum.</p> | |
<div class="note-actions"> | |
<button class="edit-note btn btn-sm btn-secondary">edit</button> | |
<button class="delete-note btn btn-sm btn-secondary">delete</button> | |
</div> | |
</div> | |
<div class="note"> | |
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse cursus mi in ipsum cursus congue. Morbi molestie lorem nisl, vel tristique lorem pulvinar sed. Phasellus laoreet cursus felis, sollicitudin faucibus nisi lacinia non. Aliquam erat volutpat. Sed finibus sodales urna sit amet rutrum. Nullam accumsan est a porttitor viverra. Etiam pretium eros ipsum, non aliquet elit scelerisque ut. Nulla vulputate turpis eget libero pulvinar, aliquam mollis nibh interdum. Cras et nunc vel odio ornare condimentum in nec mauris. Suspendisse non magna ligula. Pellentesque sollicitudin massa ac elit elementum, quis volutpat justo elementum.</p> | |
<div class="note-actions"> | |
<button class="edit-note btn btn-sm btn-secondary">edit</button> | |
<button class="delete-note btn btn-sm btn-secondary">delete</button> | |
</div> | |
</div> | |
</div> <!-- note grid --> | |
</div> <!-- container --> | |
<!-- Scripts --> | |
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script> | |
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script> | |
<script src="idb-demo.js"></script> | |
</body> | |
</html> |
jQuery(document).ready(function($){ | |
console.log('DOM Ready'); | |
let db; | |
let openRequest = indexedDB.open('notes', 1); | |
openRequest.onupgradeneeded = function(e){ | |
console.log('Upgrading DB...'); | |
let thisDB = e.target.result; | |
if (!thisDB.objectStoreNames.contains('notestore')) { | |
thisDB.createObjectStore('notestore', { autoIncrement: true }); | |
} | |
}; | |
openRequest.onsuccess = function(e){ | |
console.log('DB Open Success!'); | |
db = e.target.result; | |
$('#addbtn').click(function(e) { | |
let text = prompt('Enter the text for the note.'); | |
console.log(text); | |
if (text) { | |
addNote(text); | |
} else { | |
alert('Note text must not be blank.'); | |
} | |
}); | |
renderNotes(); | |
} | |
openRequest.onerror = function(e){ | |
console.log('Error'); | |
// Handle errors | |
}; | |
// render notes function | |
function renderNotes(){ | |
$('#note-grid').empty(); | |
//Count Objects | |
let transaction = db.transaction(['notestore'], 'readonly'); | |
let store = transaction.objectStore('notestore'); | |
let countRequest = store.count(); | |
countRequest.onsuccess = function(e){ | |
console.log(countRequest.result); | |
let count = Number(countRequest.result); | |
if (count > 0) { | |
let objectStore = db.transaction(['notestore'], 'readonly').objectStore('notestore'); | |
objectStore.openCursor().onsuccess = function(e){ | |
let cursor = e.target.result; | |
if (cursor) { | |
let key = cursor.key; | |
let $wrapper = $('<div data-key="' + key + '" class="note">'); | |
let $contents = $('<p>' + cursor.value + '</p>'); | |
let $actions = $('<div class="note-actions">'); | |
let $deleteBtn = $('<button class="delete-note btn btn-sm btn-secondary">delete</button>'); | |
$deleteBtn.click(function(e){ | |
console.log('Delete ' + cursor.key); | |
deleteNote(key); | |
}); | |
let $editBtn = $('<button class="edit-note btn btn-sm btn-secondary">edit</button>'); | |
$editBtn.click(function(e){ | |
console.log('Edit ' + cursor.key); | |
editNote(key); | |
}); | |
$actions.append($editBtn); | |
$actions.append($deleteBtn); | |
$wrapper.append($contents); | |
$wrapper.append($actions); | |
$('#note-grid').append($wrapper); | |
cursor.continue(); | |
} else { | |
//no more entries | |
} | |
}; | |
} else { | |
$('#note-grid').empty(); | |
} | |
}; | |
} | |
// add note function | |
function addNote(text) { | |
console.log('Add: ' + text); | |
let transaction = db.transaction(['notestore'],'readwrite'); | |
let store = transaction.objectStore('notestore'); | |
let addRequest = store.add(text); | |
addRequest.onerror = function(e) { | |
console.log("Error", e.target.error.name); | |
//some type of error handler | |
} | |
addRequest.onsuccess = function(e) { | |
console.log("added note"); | |
renderNotes(); | |
} | |
} | |
// delete note by key | |
function deleteNote(k) { | |
console.log('delete pushed'); | |
let transaction = db.transaction(['notestore'], 'readwrite'); | |
let store = transaction.objectStore('notestore'); | |
let request = store.delete(k); | |
request.onsuccess = function(e){ | |
renderNotes(); | |
}; | |
} | |
// edit note by key | |
function editNote(k) { | |
console.log('edit pushed'); | |
let transaction = db.transaction(['notestore'], 'readwrite'); | |
let store = transaction.objectStore('notestore'); | |
let request = store.get(k); | |
request.onsuccess = function(e) { | |
let newtext = prompt('Enter the text you would like to update.', request.result); | |
let updateRequest = store.put(newtext, k); | |
updateRequest.onsuccess = function(e){ | |
renderNotes(); | |
}; | |
}; | |
request.onerror = function(e) { | |
// Handle errors! | |
}; | |
} | |
}); //end dom ready |
body { | |
margin-top: 20px; | |
} | |
.note-actions { | |
text-align: right; | |
} | |
.note-actions button { | |
margin: 5px 5px; | |
} | |
/* Grid Styles */ | |
@supports (display: grid) { | |
#note-grid { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); | |
grid-gap: 20px; | |
grid-auto-flow: dense; | |
} | |
.note { | |
background-color:yellow; | |
padding: 15px; | |
} | |
} |
# File Access
Allows you to interact with local files using the File API
允许您使用文件 API 与本地文件交互Can access the file attributes and its data in JavaScript using the
FileReader
interface.
可以在 JavaScript 中使用FileReader
接口访问文件属性及其数据。Does not allow you to access files on the user's computer without the user providing them to you with an input of
type=file
or using the drag and drop api.
不允许您访问用户计算机上的文件,除非用户提供type=file
的输入或使用拖放 api。https://www.html5rocks.com/en/tutorials/file/dndfiles/
https://web.dev/read-files/
https://developer.mozilla.org/en-US/docs/Web/API/File
https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications
# DEMO
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<title>File(s) size</title> | |
</head> | |
<body> | |
<form name="uploadForm"> | |
<div> | |
<input id="uploadInput" type="file" name="myFiles" multiple><br/> | |
selected files: <span id="fileNum">0</span>; | |
total size: <span id="fileSize">0</span> | |
</div> | |
<div><input type="submit" value="Send file"></div> | |
</form> | |
<script> | |
function updateSize() { | |
let nBytes = 0, | |
oFiles = this.files, | |
nFiles = oFiles.length; | |
for (let nFileId = 0; nFileId < nFiles; nFileId++) { | |
nBytes += oFiles[nFileId].size; | |
} | |
let sOutput = nBytes + " bytes"; | |
// optional code for multiples approximation | |
const aMultiples = ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"]; | |
for (nMultiple = 0, nApprox = nBytes / 1024; nApprox > 1; nApprox /= 1024, nMultiple++) { | |
sOutput = nApprox.toFixed(3) + " " + aMultiples[nMultiple] + " (" + nBytes + " bytes)"; | |
} | |
// end of optional code | |
document.getElementById("fileNum").innerHTML = nFiles; | |
document.getElementById("fileSize").innerHTML = sOutput; | |
} | |
document.getElementById("uploadInput").addEventListener("change", updateSize, false); | |
</script> | |
</body> | |
</html> |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<title>File image thumbnail</title> | |
</head> | |
<body> | |
<form name="uploadForm"> | |
<div> | |
<input id="uploadInput" type="file" name="myFiles" multiple> | |
</div> | |
</form> | |
<div id="results"></div> | |
<script> | |
function processFiles() { | |
const fileList = this.files; | |
console.log(fileList); | |
const results = document.getElementById('results'); | |
results.innerHTML = ''; | |
const numFilesHeadline = document.createElement('h3'); | |
numFilesHeadline.appendChild(document.createTextNode('Number of files: ' + fileList.length)); | |
results.appendChild(numFilesHeadline); | |
const list = document.createElement('ul'); | |
for (let i = 0; i < fileList.length; i++) { | |
const aFile = fileList[i]; | |
if (aFile.type.startsWith('image/')) { | |
const img = document.createElement('img'); | |
img.style.maxWidth = '200px'; | |
const reader = new FileReader(); | |
reader.onload = function(e) { | |
console.log(e); | |
img.src = e.target.result; | |
const item = document.createElement('li'); | |
item.appendChild(img); | |
list.appendChild(item); | |
}; | |
reader.readAsDataURL(aFile); | |
} else if (aFile.type.startsWith('text/')) { | |
const reader = new FileReader(); | |
reader.onload = function(e) { | |
console.log(e); | |
const item = document.createElement('li'); | |
item.appendChild(document.createTextNode(e.target.result)); | |
list.appendChild(item); | |
}; | |
reader.readAsText(aFile); | |
} else { | |
const item = document.createElement('li'); | |
item.appendChild(document.createTextNode('File is not an known image type.')); | |
list.appendChild(item); | |
} | |
} //end loop | |
results.appendChild(list); | |
} //end function | |
document.getElementById("uploadInput").addEventListener("change", processFiles, false); | |
</script> | |
</body> | |
</html> |