• 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 use JSON.stringify() and JSON.parse() .
    重要的是,如果想存储一个 JavaScript 对象,必须使用 JSON.stringify()JSON.parse()

  • Different than cookies as this is a JavaScript API
    与 cookie 不同,这是一个 JavaScript API

  • There 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 10MB

  • https://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 just localStorage

  • 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
    使用大量函数回调的异步 API

  • Storage 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

index
<!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>
idb-demo
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
idb-demo
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

file
<!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>
file list
<!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>