HTML 5 stockage local

Similar documents
HTML5 Offline Data. INF5750/ Lecture 6 (Part II)

Introduction Les failles les plus courantes Les injections SQL. Failles Web. Maxime Arthaud. net7. Jeudi 03 avril 2014.

Web Browser. Fetches/displays documents from web servers. Mosaic 1993

Développement Web 2. Node.js Installation de modules

Modern Web Development From Angle Brackets to Web Sockets

Mobile Web Applications using HTML5. L. Cotfas 14 Dec. 2011

Internet Explorer 10 Internet Explorer 11. Internet Explorer 10

Thursday, February 7, DOM via PHP

Safari Client-Side Storage and Offline Applications Programming Guide

Sync your schedule and work orders with SME & Microsoft Outlook

JavaScript Programming

Group Projects M1 - Cubbyhole

Client-Side Storage in Web Applications

Web Tracking for You. Gregory Fleischer

Learning HTML5 Game Programming

Brazil + JDBC Juin 2001, douin@cnam.fr

Introduction to Tizen SDK Alpha. Taiho Choi Samsung Electronics

Guide 3 - SkyDrive Pro

The Need For Speed. leads to PostgreSQL. Dimitri Fontaine 28 Mars 2013

Setting up a monitoring and remote control tool

HTML5 and Device APIs for Automotive: Is it time to power Infotainment and Car Portal Applications with Web Technologies?

Intro to Web Development

Créez les interfaces du futur avec les APIs d aujourd hui. Thursday, October 18, 12

Grapevine Mail User Guide

WEB DEVELOPMENT COURSE (PHP/ MYSQL)

Step into the Future: HTML5 and its Impact on SSL VPNs

Programming in HTML5 with JavaScript and CSS3

HTML5 the new. standard for Interactive Web

Durée 4 jours. Pré-requis

Firefox for Android. Reviewer s Guide. Contact us: press@mozilla.com

WebLogic & Coherence. Best backend for Mobile Apps. July 2014 INSERT PRESENTER TITLE AND DATE

Tizen Web Runtime Update. Ming Jin Samsung Electronics

Website Report: To-Do Tasks: 22 SEO SCORE: 73 / 100. Add the exact keywords to this URL.

Research of Web Real-Time Communication Based on Web Socket

Stockage distribué sous Linux

Smartphone Enterprise Application Integration

Vincent Rullier Technology specialist Microsoft Suisse Romande

Administrer les solutions Citrix XenApp et XenDesktop 7.6 CXD-203

Tool & Asset Manager 2.0. User's guide 2015

You can choose to install the plugin through Magento Connect or by directly using the archive files.

Solaris 10 Documentation README

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";! SET time_zone = "+00:00";!

Audit de sécurité avec Backtrack 5

BGP dans le ToR. FRnOG 23. Antoine Sibout Datacenter Architect Juniper Networks

Adding Outlook to a Blackberry, Downloading, Installing and Configuring Blackberry Desktop Manager

About the To-Do Bar in Outlook 2007

Les Support Packs IA94 et IA9H

GET /FB/index.html HTTP/1.1 Host: lmi32.cnam.fr

Getting Started with Android Programming (5 days) with Android 4.3 Jelly Bean

HTML5 Websockets with ruby and rails. Saurabh Bhatia, CEO, Safew Labs

Introduction au BIM. ESEB Seyssinet-Pariset Economie de la construction contact@eseb.fr

Neoteris IVE Integration Guide

The Risks of Client-Side Data Storage From cookie to database

Admin Guide Product version: Product date: November, Technical Administration Guide. General

Configuring your client to connect to your Exchange mailbox

Upgrade to Microsoft Web Applications

Tizen Web Runtime. Device API

Web Interface using HTML5 for Interaction between Mobile Device & Cloud- Services

Modern Web Application Framework Python, SQL Alchemy, Jinja2 & Flask

Locked Web Browser. User s Manual

Annexe - OAuth Introduction. Xavier de Rochefort xderoche@labri.fr - labri.fr/~xderoche 15 mai 2014

Development Techniques for Native/Hybrid Tizen Apps. Presented by Kirill Kruchinkin

Feature Integration Across Microsoft Office Server Products SharePoint Server, Exchange Server, Lync Server, and Office Web Apps

Real Time Data in Web Applications

Neoteris IVE Integration Guide

Seagate NAS OS 4 Reviewers Guide: NAS / NAS Pro / Business Storage Rackmounts

Brainshark/Salesforce.com Integration Installation Procedures

INTEGRATING MICROSOFT DYNAMICS CRM WITH SIMEGO DS3

WebSocket Server. To understand the Wakanda Server side WebSocket support, it is important to identify the different parts and how they interact:

Wildix WebRTC Kite & MCU Videoconference. Quick Start Guide

How To Write A Web Server In Javascript

Initial Setup of Microsoft Outlook with Google Apps Sync for Windows 7. Initial Setup of Microsoft Outlook with Google Apps Sync for Windows 7

Document OwnCloud Collaboration Server (DOCS) User Manual. How to Access Document Storage

ISL Online Integration Manual

ios SDK possibilities & limitations

Memo bconsole. Memo bconsole

Vodafone Plus. User Guide for Windows Mobile

Frequently Asked Questions for the USA TODAY e-newspaper

GCM GOOGLE CLOUD MESSAGING

Working with your NTU off campus

Mobile development with Apache OFBiz. Ean Schuessler, Brainfood

Accessing External Databases from Mobile Applications

Hours: The hours for the class are divided between practicum and in-class activities. The dates and hours are as follows:

Saruman Documentation

READ AND FOLLOW ALL SAFETY INSTRUCTIONS 1. DANGER RISK OF SHOCK DISCONNECT POWER BEFORE INSTALLATION

Assignment # 1 (Cloud Computing Security)

Short Form Description / Sommaire: Carrying on a prescribed activity without or contrary to a licence

Learning Web App Development

Monitoring Open Source pour Java avec JmxTrans, Graphite et Nagios

Document From MAXIMUM BUSINESS INFORMATION TECHNOLOGY ON A. OwnCloud User Manual. TO I Cafe`

Transcription:

HTML 5 stockage local & synchronisation Raphaël Rougeron @goldoraf

Une mutation est en cours Pages HTML Serveur Client JSON Single-Page Application

Form validation Drag n' Drop API Geolocation Web Notifications WebGL Video Canvas Audio File API Web Workers Web Sockets

Stockage local

1er bénéfice : le offline?

1er bénéfice : économiser le serveur! Source : HTML5 Web Sockets: A Quantum Leap in Scalability for the Web http://soa.sys-con.com/node/1315473

4 étapes

Etape 1 Rendre les ressources disponibles hors connexion

Application Cache <html <html manifest"cache.manifest"> manifest"cache.manifest"> CACHE MANIFEST CACHE: index.html css/style.css img/logo.png js/main.js text/cache manifest

! Avoir un manifeste change complètement les règles d'accès aux ressources

CACHE MANIFEST # v.1.2 build1234 CACHE: index.html css/style.css img/logo.png js/main.js

CACHE CACHE MANIFEST MANIFEST # v.1.2 build1234 CACHE: CACHE: index.html css/style.css img/logo.png js/main.js NETWORK NETWORK /sync /sync /api/* /api/* http://api.twitter.com

CACHE CACHE MANIFEST MANIFEST # v.1.2 build1234 v.1.2 build1234 CACHE: CACHE: index.html index.html css/style.css css/style.css img/logo.png img/logo.png js/main.js js/main.js NETWORK NETWORK /sync /sync /api/* /api/* http://api.twitter.com http://api.twitter.com FALLBACK FALLBACK images/large/ images/large/ images/offline.jpg images/offline.jpg *.html *.html /offline.html /offline.html

window.applicationcache status UNCACHED IDLE CHECKING DOWNLOADING UPDATEREADY OBSOLETE

appcache appcache window.applicationcache; window.applicationcache; appcache.addeventlistener('updateready', appcache.addeventlistener('updateready', function(e) function(e) if if (appcache.status (appcache.status appcache.updateready) appcache.updateready) appcache.swapcache(); appcache.swapcache(); if if (confirm('une (confirm('une nouvelle nouvelle version version est est dispo')) dispo')) window.location.reload(); window.location.reload(); } } } } }, }, false); false);

> 10? > 3.5 > 4.0 > 4.0 > 10.6 > 3.2 > 2.1 > 11.0

Etape 2 Découpler l'application du réseau

Todo Backbone.Model.extend( Todo Backbone.Model.extend( toggle: function() toggle: function() this.save(done:!this.get("done")}); this.save(done:!this.get("done")}); }, }, clear: function() clear: function() this.destroy(); this.destroy(); } } }); }); TodoList Backbone.Collection.extend( TodoList Backbone.Collection.extend( model: Todo, model: Todo, done: function() done: function() return this.filter(function(todo) return todo.get('done'); }); return this.filter(function(todo) return todo.get('done'); }); }, }, remaining: function() remaining: function() return this.without.apply(this, this.done()); return this.without.apply(this, this.done()); } } }); });

Backbone.Sync create() read() update() POST GET PUT /collection /collection[/id] /collection/id delete() DELETE /collection/id

Etape 3 Stocker localement les données

3 options

FileSystem API! seulement

window.requestfilesystem window.requestfilesystem window.requestfilesystem window.requestfilesystem window.webkitrequestfilesystem; window.webkitrequestfilesystem; window.requestfilesystem(window.temporary, window.requestfilesystem(window.temporary, 5*1024*1024, 5*1024*1024, oninitfs, oninitfs, errorhandler); errorhandler); window.webkitstorageinfo.requestquota(window.persistent, window.webkitstorageinfo.requestquota(window.persistent, 5*1024*1024, 5*1024*1024, initfs, initfs, errorhandler); errorhandler); function function initfs(grantedbytes) initfs(grantedbytes) window.requestfilesystem(persistent, window.requestfilesystem(persistent, grantedbytes, grantedbytes, oninitfs, oninitfs, errorhandler); errorhandler); } }

FileError code QUOTA_EXCEEDED_ERR NOT_FOUND_ERR SECURITY_ERR INVALID_MODIFICATION_ERR INVALID_STATE_ERR

function function oninitfs(fs) oninitfs(fs) fs.root.getfile('data.txt', fs.root.getfile('data.txt', create: create: true}, true}, function(entry) function(entry) entry.file(function(file) entry.file(function(file) reader reader new new FileReader(); FileReader(); reader.onloadend reader.onloadend function(e) function(e) repository.init(json.parse(e.target.result)); repository.init(json.parse(e.target.result)); }; }; reader.readastext(file); reader.readastext(file); }, }, errorhandler); errorhandler); }, }, errorhandler); errorhandler); } }

FileEntry name fullpath isfile isdirectory file() createwriter() moveto() copyto() remove()...

function function persistdata(fs, persistdata(fs, data) data) fs.root.getfile('log.txt', fs.root.getfile('log.txt', create: create: true}, true}, function(entry) function(entry) entry.createwriter(function(writer) entry.createwriter(function(writer) writer.onwriteend writer.onwriteend function(e) function(e) console.log('write console.log('write completed.'); completed.'); }; }; writer.onerror writer.onerror function(e) function(e) console.log('write console.log('write failed: failed: ' ' + + e.tostring()); e.tostring()); }; }; bb bb new new BlobBuilder(); BlobBuilder(); bb.append(json.stringify(data)); bb.append(json.stringify(data)); writer.write(bb.getblob('text/plain')); writer.write(bb.getblob('text/plain')); }, }, errorhandler); errorhandler); }, }, errorhandler); errorhandler); } }

Web Storage localstorage et sessionstorage

Un simple dépôt clé-valeur localstorage.setitem("foo", localstorage.setitem("foo", "bar"); "bar"); localstorage.setitem("tweets", localstorage.setitem("tweets", JSON.stringify(tweets); JSON.stringify(tweets); tweets tweets JSON.parse(localStorage.getItem("tweets")); JSON.parse(localStorage.getItem("tweets"));

! Quota de 5 Mo Pas de transactions Pas d'indexation

localstorage["tweets:1234"] localstorage["tweets:1234"] "Lorem "Lorem ipsum..."; ipsum..."; localstorage["tweets:1235"] localstorage["tweets:1235"] "Nam "Nam mauris mauris lorem..."; lorem..."; localstorage["tags"] localstorage["tags"] JSON.stringify(["Java", JSON.stringify(["Java", "Python", "Python", "Ruby", "Ruby", "PHP"]); "PHP"]); localstorage["tags:java"] localstorage["tags:java"] JSON.stringify([1234, JSON.stringify([1234, 1235]); 1235]);

Très bien supporté API simple Mis en avant par de nb compagnies/projets Performances API synchrone Sérialisation Requêtes Complexité "indexation" manuelle Maintien intégrité

> 8 > 3.5 > 4.0 > 4.0 > 10.5 > 3.2 > 2.1 > 11.0

localstorage adapter Backbone.sync function(method, model, options) Backbone.sync function(method, model, options) resp; resp; store model.localstorage model.collection.localstorage; store model.localstorage model.collection.localstorage; switch (method) switch (method) case "read": resp model.id? store.find(model) : case "read": resp model.id? store.find(model) : store.findall(); store.findall(); break; break; case "create": resp store.create(model); case "create": resp store.create(model); break; break; case "update": resp store.update(model); case "update": resp store.update(model); break; break; case "delete": resp store.destroy(model); case "delete": resp store.destroy(model); break; break; } } if (resp) if (resp) options.success(resp); options.success(resp); } else } else options.error("record not found"); options.error("record not found"); } } }; };

Petit intermède Vous connaissez Redis? BankersBox bb new BankersBox(1); bb new BankersBox(1); bb.set("foo", "bar"); bb.set("foo", "bar"); bb.get("foo"); // returns "bar" bb.get("foo"); // returns "bar" bb.set("count", 10); bb.set("count", 10); bb.incr("count"); // sets "count" to 11, returns 11 bb.incr("count"); // sets "count" to 11, returns 11 bb.incr("newcount"); // sets "newcount" to 1, returns 1 bb.incr("newcount"); // sets "newcount" to 1, returns 1 bb.lpush("mylist", "hello"); bb.lpush("mylist", "hello"); bb.lrange("mylist", 0, 1); // returns ["hello"] bb.lrange("mylist", 0, 1); // returns ["hello"] bb.rpush("mylist", "world"); bb.rpush("mylist", "world"); bb.lrange("mylist", 0, 1); // returns ["hello", "world"] bb.lrange("mylist", 0, 1); // returns ["hello", "world"] bb.sadd("myset", "apple"); bb.sadd("myset", "apple"); bb.sadd("myset", "oragne"); bb.sadd("myset", "oragne");

IndexedDB

Préfixes, préfixes... window.indexeddb window.indexeddb window.indexeddb window.indexeddb window.mozindexeddb window.mozindexeddb window.webkitindexeddb; window.webkitindexeddb; window.idbkeyrange window.idbkeyrange window.idbkeyrange window.idbkeyrange window.webkitidbkeyrange; window.webkitidbkeyrange; window.idbtransaction window.idbtransaction window.idbtransaction window.idbtransaction window.webkitidbtransaction; window.webkitidbtransaction;

API asynchrone function function IndexedDBAdapter() IndexedDBAdapter() this.db this.db null; null; request request indexeddb.open("contactapp"); indexeddb.open("contactapp"); request.onsuccess request.onsuccess function(e) function(e) this.db this.db e.target.result; e.target.result; }.bind(this); }.bind(this); request.onfailure request.onfailure function(e) function(e) console.log("could console.log("could not not connect connect to to the the database"); database"); } } }; };

Création d'un dépôt IndexedDBAdapter.prototype.create IndexedDBAdapter.prototype.create function(storename) function(storename) v v "1.0"; "1.0"; request request this.db.setversion(v); this.db.setversion(v); request.onsuccess request.onsuccess function(e) function(e) store store this.db.createobjectstore(storename, this.db.createobjectstore(storename, "keypath": "keypath": "id", "id", "autoincrement": "autoincrement": true true }); }); }; }; request.onblocked request.onblocked function(e) function(e) console.log("the console.log("the database database is is open open in in another another tab."); tab."); }; }; }; };

Persistence d'un objet IndexedDBAdapter.prototype.save(storeName, IndexedDBAdapter.prototype.save(storeName, object, object, callback) callback) trans trans db.transaction([storename], db.transaction([storename], IDBTransaction.READ_WRITE, IDBTransaction.READ_WRITE, 0); 0); store store trans.objectstore(storename); trans.objectstore(storename); request request store.put(object); store.put(object); request.onsuccess request.onsuccess function(e) function(e) callback(object); callback(object); }; }; }; };

Requête simple IndexedDBAdapter.prototype.all(storeName, IndexedDBAdapter.prototype.all(storeName, callback) callback) trans trans db.transaction([storename], db.transaction([storename], IDBTransaction.READ_WRITE, IDBTransaction.READ_WRITE, 0); 0); store store trans.objectstore(storename); trans.objectstore(storename); keyrange keyrange IDBKeyRange.lowerBound(0); IDBKeyRange.lowerBound(0); request request store.opencursor(keyrange); store.opencursor(keyrange); request.onsuccess request.onsuccess function(e) function(e) cursor cursor e.target.result; e.target.result; if if (cursor) (cursor) callback(cursor.value); callback(cursor.value); cursor.continue(); cursor.continue(); } } }; }; }; };

Key Ranges IDBKeyRange.upperBound(x); IDBKeyRange.upperBound(x); // // < < x x IDBKeyRange.upperBound(x, IDBKeyRange.upperBound(x, true); true); // // < < x x IDBKeyRange.lowerBound(y); IDBKeyRange.lowerBound(y); // // > > y y IDBKeyRange.lowerBound(y, IDBKeyRange.lowerBound(y, true); true); // // > > y y IDBKeyRange.bound(x, IDBKeyRange.bound(x, y); y); // // > > x x && && < < y y IDBKeyRange.bound(x, IDBKeyRange.bound(x, y, y, true, true, false); false); // // > > x x && && < < y y

Index contacts contacts [ [ firstname: firstname: "John", "John", lastname: lastname: "Doe", "Doe", email: email: "jdoe@zz.com" "jdoe@zz.com" }, }, firstname: firstname: "Jane", "Jane", lastname: lastname: "Doe", "Doe", email: email: "jane@zz.com" "jane@zz.com" }, }, firstname: firstname: "Johnny", "Johnny", lastname: lastname: "Carson", "Carson", email: email: "jca@zz.com" "jca@zz.com" } } ]; ]; objectstore.createindex("firstname", objectstore.createindex("firstname", "firstname", "firstname", unique: unique: false false }); }); objectstore.createindex("lastname", objectstore.createindex("lastname", "lastname", "lastname", unique: unique: false false }); }); objectstore.createindex("email", objectstore.createindex("email", "email", "email", unique: unique: true true }); });

Index contacts contacts [ [ firstname: firstname: "John", "John", lastname: lastname: "Doe", "Doe", email: email: "jdoe@zz.com" "jdoe@zz.com" }, }, firstname: firstname: "Jane", "Jane", lastname: lastname: "Doe", "Doe", email: email: "jane@zz.com" "jane@zz.com" }, }, firstname: firstname: "Johnny", "Johnny", lastname: lastname: "Carson", "Carson", email: email: "jca@zz.com" "jca@zz.com" } } ]; ]; index index objectstore.index("lastname"); objectstore.index("lastname"); index.opencursor(idbkeyrange.only("doe")).onsuccess index.opencursor(idbkeyrange.only("doe")).onsuccess function(e) function(e) cursor cursor e.target.result; e.target.result; if if (cursor) (cursor) callback(cursor.value); callback(cursor.value); cursor.continue(); cursor.continue(); } } }; }; index index objectstore.index("firstname"); objectstore.index("firstname"); index.opencursor(idbkeyrange.lowerbound("john")).onsuccess index.opencursor(idbkeyrange.lowerbound("john")).onsuccess function(e) function(e)...... }; };

> 10? > 8.0 > 16.0?????

Et pourquoi pas Web SQL Database?

X X > 4.0 > 3.1 > 10.5 > 3.2 > 2.1 > 11.0

Le polyfill ultime? Lawnchair localstorage indexeddb webkit sqlite gears sqlite ie userdata backberry persistent store window name

Etape 4 Développer une stratégie de synchronisation

L'exemple d'activesync

POST /Microsoft Server ActiveSync?Userjdoe@zz.com&... &Cmd<Commande> CmdFolderSync&SyncKey123456789 } folders: [ contacts: id: 1234, created: 0, 0, updated: 2, 2, deleted: 1 }, }, todos: id: 5678, created: 5, 5, updated: 0, 0, deleted: 2 } ]

CmdSync&SyncKey123456789&FolderId5678 } synckey: 987654321, created: [ id: 4321, label: "Acheter le le pain" }, },... ], ], updated: [... ], ], deleted: [ 5678, 7890 ]

Autres pistes Mozilla Services SyncML Operational Transformation ShareJS

Etape subsidiaire Le "push"

Polling Client Serveur n secondes n secondes

Long polling (COMET) Client Serveur Événement côté serveur Événement côté serveur

WebSockets

Handshake GET /resource name/ HTTP/1.1 Upgrade: WebSocket Connection: Upgrade Host: /server/ Origin: /origin/ WebSocket Protocol: /protocol/ HTTP/1.1 101 Web Socket Protocol Handshake Upgrade: WebSocket Connection: Upgrade WebSocket Origin: /origin/ WebSocket Location: /url/ WebSocket Protocol: /subprotocol/

Serveur (avec node.js) io io require('socket.io').listen(80); io.sockets.on('connection', function (socket) socket.emit('news', hello: 'world' }); socket.on('my other event', function (data) console.log(data); }); }); Client socket io.connect('http://localhost'); socket.on('news', function (data) console.log(data); socket.emit('my other event', my: 'data' }); });

> 10? > 6.0 > 14.0 > 5.0 > 11.0 > 4.2? > 11.0

Avec Socket.IO > 5.5 > 3.0 > 4.0 > 3.0 > 10.61 > 3.2 > 2.1 > 11.0

Server-Sent Events

HTTP traditionnel GET /myappstream Content Type: text/event stream

if if (window.eventsource) source new EventSource('myAppStream'); } else // // fallback to to something... } source.addeventlistener('message', function(e) console.log(e.data); }, }, false); source.addeventlistener('open', function(e) }, }, false); source.addeventlistener('error', function(e) if if (e.readystate EventSource.CLOSED) } }, }, false);

Format des messages data: Hello world\n\n Multi-ligne data: Hello\n data: world\n\n JSON data: \n data: "msg": "Hello world",\n data: "id": 12345\n data: }\n\n

Format des messages Utilisation des IDs id: 12345\n data: "user": "goldo", "msg": "Hello!"}\n\n Utilisation des events data: "msg": "Hello!"}\n\n event: login\n data: "user": "goldo"}\n\n event: update\n data: "user": "goldo", "status": "away"}\n\n

Avantages Reconnexion automatique Ids Events

Questions / réponses