提供离线数据编辑和数据同步

本文探讨HTML5的离线应用支持和本地持久存储功能,通过Contact Manager应用程序示例展示了如何创建、读取、更新和删除本地数据,并在重新上线时同步到服务器。文章还介绍了HTML5的localStorage、WebSQL和Indexed DB等存储技术,以及如何构建响应在线/离线状态的UI。
摘要由CSDN通过智能技术生成

HTML版本5(HTML5)有望在2014年之前达到万维网联盟(W3C)的推荐状态。尽管它不是正式的标准,但网络浏览器供应商正在添加和营销HTML5功能。 HTML5已经在重塑Internet网站和业务线(LOB)应用程序的Web体验。 许多网站,例如Amazon Kindle Cloud Reader,已经在利用HTML5。 HTML5的两个关键功能将极大地改变LOB应用程序:脱机应用程序支持和本地持久存储。 由于HTML5不是官方标准,因此浏览器支持最多是不一致的。

在本文中,了解脱机应用程序支持以及建议HTML5标准提供的各种持久存储功能。 一个示例应用程序有助于说明这些功能。

应用范例

Contact Manager应用程序示例提供了联系人信息(姓名,地址和电话号码)的管理。 它提供了联机模式,脱机模式和简单的数据同步功能,以在切换到联机模式时将本地数据更改同步到服务器。 脱机时,数据驻留在本地持久存储中。 该应用程序在联机和脱机模式下均支持四种基本的持久存储功能-创建,读取,更新和删除(CRUD)。

建筑

图1显示了Contact Manager应用程序体系结构的概述。 服务器体系结构由两个servlet组成:业务服务和数据提供程序。 用户界面(UI)包含一个HTML文件,四个JavaScript模块以及对jQuery库最新版本的外部引用。

图1.应用程序架构概述
该图显示了应用程序架构

数据模型

数据模型由两个数据实体(联系和状态)组成, 如图2所示。 联系人表包含实际联系人数据; 状态表包含状态选择列表的字典值。

图2.数据模型
该图显示了数据模型

服务器界面

服务器接口由两个servlet组成: ContactServletDictionaryServlet 。 表1总结了这些servlet。 (servlet的实现以及相应的业务服务和数据提供程序不在本文讨论范围之内。)

表1. servlet的摘要
Servlet名称 操作方式 参量 描述
DictionaryServlet代码> getstates 不适用 以JavaScript对象符号(JSON)格式返回状态数组。
ContactServlet getallcontacts 不适用 返回JSON格式的联系人记录数组。
ContactServlet delete contactId要删除的联系人的ID。 删除指定的联系人记录; 返回带有布尔值标志的JSON对象,以指示操作是否成功。
"{"result": true/false"}
ContactServlet save
  • contactId要保存的联系人的ID。 (如果大于零,则是更新操作。)
  • firstName名字字段的值。
  • lastName姓氏字段的值。
  • street1街道1字段的值。
  • street2街道2字段的值。
  • city -城市字段值。
  • state -该状态字段值。
  • zipCode邮政编码字段的值。
返回带有布尔值标志的JSON对象,以指示操作是否成功以及新的或更新的联系人ID。
"{"contactId": <id>, "result": <true/false>"}

调用服务器界面

清单1中的代码显示了如何对联系人servlet进行异步调用以检索存储在在线数据库中的联系人。 该代码使用jQuery getJSON函数来调用联系人servlet。

清单1.从服务器检索联系人
function loadOnlineContacts() 
{
	$('#contactList').empty();
	$('#contactList').append('Loading contact data...');
	
	var url = '/html5app/contact?operation=getallcontacts';
	
	$.getJSON(url, function(data) {
		saveOfflineContactData(data);
		displayContactData(data);		
	});
}

清单2中的代码显示了如何将新的或更新的联系人保存到服务器。 它使用jQuery ajax函数。 该代码使用HTTP POST将数据发送到联系人servlet。

清单2.将联系人保存到服务器
function postEditedContact(dataString) {
	postEditedContact(dataString, false);	
}

function postEditedContact(dataString, suppressAlert) {
var contactId = $('input[name="contactId"]')[0].value;

$.ajax({  
type: "POST",  
url: "/html5app/contact",  
data: dataString,  
cache: false,
dataType: "json",
success: function(data) {
	var result = data.result;
	
	if (result) 
	{
		if (contactId > 0)
		{
		if (!suppressAlert) {
		alert("Contact was successfully updated.");
		}
		var lastModifyDate = data.lastModifyDate;
		$('input[name="lastModifyDate"]')[0].value = lastModifyDate;
		}
		else 
		{
		if (!suppressAlert) {
			alert("Contact was successfully created.");
		}
		
		var lastModifyDate = data.lastModifyDate;
		$('input[name="lastModifyDate"]')[0].value = lastModifyDate;
		$('input[name="contactId"]')[0].value = data.contactId;
		}
		
		loadOnlineContacts();

		hideEditForm();
	}
	else 
	{
		alert('An error occurred saving contact ' + contactId + '.');
	}
}
});  
}

最后一个功能是从在线数据库中删除一条记录。 清单3显示了如何从服务器删除记录。 该代码使用jQuery getJSON函数来调用联系人servlet。

清单3.删除服务器上的联系人
function deleteOnlineContact(contactId, suppressAlert){
	var url = '/html5app/contact?operation=delete&contactId=' + contactId;
	
	$.getJSON(url, function(data) {
		var result = data.result;
		if (result) {
			if (!suppressAlert) {
				alert('Contact deleted');
			}
			loadOnlineContacts();
		}
		else {
			alert('Contact ' + contactId + 'not deleted');
		}
	});		
}

建立本地数据提供者

本地数据提供者在本地保留所有选择列表和联系数据。 HTML5规范包含一些用于持久存储的选项。 选择使用哪种技术取决于您的数据存储和浏览器支持要求。 以下各节讨论了三种持久性存储技术以及使用所有主要Web浏览器支持的持久性存储技术的本地数据提供程序的实现。

HTML5与以下三种持久存储技术相关:

  • localStorage -localStorage使用平面键值存储提供了简单的数据存储。 所有主要的Web浏览器,包括Apple®Safari®,Google Chrome™,Microsoft®Windows®InternetExplorer®,Mozilla®Firefox®和Opera™,都支持localStorage。 HTML5 localStorage是同步的,并且是当前唯一支持跨平台和跨浏览器的数据库存储机制。
  • WebSQL -WebSQL最初旨在将基于Transact-SQL的数据库引入Web浏览器。 基于与通用关系数据库(例如IBM®DB2®,Microsoft SQLServer®,Oracle®MySQL®Server和Oracle Database)的相似性,学习曲线很低。 某些浏览器(包括Safari,Chrome和Opera)都支持WebSQL。 Firefox或Internet Explorer不支持它。 据推测,由于不再开发WebSQL建议的规范,因此它将被淘汰。
  • 索引数据库(Indexed DB) -索引数据库是一个索引层次结构键值存储,类似于许多商业云数据存储产品。 WebSQL不再支持索引数据库,而Firefox,Chrome和将来的Internet Explorer 10当前都支持索引数据库。索引数据库的应用程序编程接口(API)是异步的,并支持索引,查询和事务。 。

本文中的示例解决方案将JSON和localStorage用于持久存储,这主要是因为广泛的浏览器支持localStorage。

本地数据提供商

localStorage方法通过将联系人和字典数据序列化为JSON字符串并将其保存到localStorage来保留联系人和字典数据。 检索数据时,将其反序列化为JSON对象数组并进行相应处理。

本地保存数据

清单4显示了如何将联系人数据保存到localStorage。 JavaScript函数JSON.stringify用于序列化服务器返回到字符串的JSON数据,以便可以将其存储在localStorage中。

清单4.将数据保存到localStorage
// fetch data from server
...
			
// convert JSON data to a string
var contactDataString = JSON.stringify(data);

// persist contact data to localstorage
localStorage.setItem("contactData", contactDataString);

本地检索数据

清单5显示了如何从localStorage检索数据。 第一步是从localStorage获取JSON字符串。 然后使用JavaScript函数eval将字符串转换为JSON对象,该函数将字符串反序列化为JSON对象数组。 使用自定义JavaScript函数displayContactData显示数据。

清单5.从localStorage读取数据
function loadOfflineContacts() 
{
	$('#contactList').empty();
	$('#contactList').append('Loading contact data...');
	
	var dataStr = localStorage.getItem("contactData");
	var data = eval('(' + dataStr + ')');
	
	displayContactData(data);
}

在本地删除记录

清单6显示了如何从localStorage删除记录。

清单6.从localStorage删除一条记录
function deleteOfflineContact(contactId) {
	var dataStr = localStorage.getItem("contactData");
	var data = eval('(' + dataStr + ')');
	
	var recordUpdated = false;
	$.each(data, function(i,item){
		if (item.id == contactId) {
			item.isDeleted=true;
			recordUpdated = true;
			return false;
		}	
	});
	
	if (recordUpdated) {
		dataStr = JSON.stringify(data);
		localStorage.setItem("contactData", dataStr);
		alert("Contact was successfully deleted.");
		
		loadOfflineContacts();
	}
}

代码:

  • 从本地存储读取数据库并反序列化。
  • 遍历记录,直到找到contactId记录。
  • isDeleted标志设置为true
  • 在数据同步功能中使用isDeleted标志。 (请参阅“ 数据同步 ”部分。)
  • 将数据持久保存到localStorage,并刷新数据网格。

本地更新和创建记录

清单7显示了如何在localStorage中更新或创建记录。

清单7.更新localStorage中的一条记录
function updateLocalContact() {
	var dataStr = localStorage.getItem("contactData");
	var data = eval('(' + dataStr + ')');
	
	var contactId = $('input[name="contactId"]')[0].value;
	
	var recordUpdated = false;
	if (contactId > 0) {
		$.each(data, function(i,item){
			if (item.id == contactId) {
				item.isDirty=true;
				item.firstName = $('input[name="firstName"]')[0].value;
				item.lastName = $('input[name="lastName"]')[0].value;
				item.street1 = $('input[name="street1"]')[0].value;
				item.street2 = $('input[name="street2"]')[0].value;
				item.city = $('input[name="city"]')[0].value;
				item.state = $('select[name="state"]')[0].value;
				item.zipCode = $('input[name="zipCode"]')[0].value;	
				recordUpdated = true;
				return false;
			}	
		});
	}
	else {		
		var newContactId = 0;
		var nextId = 0;
		while(newContactId == 0) {
			var found = false;

			nextId = nextId - 1;
			$.each(data, function(i,item){
				if (item.id == nextId) {
					found = true;
					return false;
				}
			});
			if (!found) {
				newContactId = nextId;
			}
		}
		var lastModifyDate = "";
		var newContact = {"street2": $('input[name="street2"]')[0].value,
				"id":newContactId,
				"street1":$('input[name="street1"]')[0].value,
				"lastName":$('input[name="lastName"]')[0].value,
				"isDirty":true,
				"zipCode":$('input[name="zipCode"]')[0].value,
				"state":$('select[name="state"]')[0].value,
				"lastModifyDate": lastModifyDate,
				"isDeleted":false,
				"firstName":$('input[name="firstName"]')[0].value,
				"city":$('input[name="city"]')[0].value};
		var nextIndex = data.length;
		data[nextIndex] = newContact;
		recordUpdated=true;
	} 
	
	if (recordUpdated) {
		dataStr = JSON.stringify(data);
		localStorage.setItem("contactData", dataStr);
		alert("Contact was successfully updated.");
	}
	
	hideEditForm();
}

如清单7所示,您:

  • 从localStorage读取数据库并反序列化。
  • 如果要保存的记录的contactId不为零(发生更新),则遍历记录直到找到contactId记录。 然后相应地进行更新。
  • 或者,如果记录是新记录( contactId不为零),则查找下一个未使用的负contactId
  • 将其分配给新记录。
  • 将新记录追加到数据库。

然后将数据序列化为JSON字符串并保存到localStorage。 服务器同步期间将分配一个有效的(大于零) contactId 。 否定ID是用于将记录标识为新记录的临时ID。

重要的是要知道localStorage:

  • 限制为5MB。 (当需要更多数据存储时,应使用索引DB。)
  • 所有主要的网络浏览器都支持。
  • 仅适用于字符串值。

下一步是使用HTML5构建UI。

使用HTML5构建UI

该示例Contact Manager应用程序具有一个只有一个页面的简单UI。 它支持编辑和删除每个记录,并提供创建新记录的功能。 级联样式表(CSS)和动态HTML(通过jQuery)用于根据需要隐藏和显示创建/编辑子表单。

为了提供一致的用户体验,无论是联机还是脱机都使用同一页面; 唯一的区别是执行操作时将调用哪个数据提供程序。 图3显示了该应用程序。

图3. Contact Manager应用程序
该图显示了Contact Manager应用程序

JavaScript模块

该应用程序包含四个自定义JavaScript模块:

  • core.js提供了常见JavaScript函数,并由其他模块使用。
  • formEvents.js提供了表单和按钮事件处理程序。 它根据联机或脱机状态将数据库操作分派给正确的数据提供者。
  • onlinedb.js提供了在线时与服务器通信的功能。
  • offlinedb.js提供本地数据存储功能。

所有模块还使用最新版本的jQuery库来遍历数据,发出异步Web请求和动态HTML。 客户端使用JSON与服务器通信。

离线应用程序清单

HTML5脱机功能提供了对静态文件和资源的缓存。 脱机应用程序清单文件(.appcache)是用于为Web应用程序启用脱机应用程序支持的关键文件。 清单文件定义以下信息:

  • 离线时哪些资源和页面可用。
  • 哪些资源仅在线可用。
  • 显示后备页面以显示离线时不可用的资源。

清单文件由三部分组成: CACHENETWORKFALLBACKCACHE下的页面和资源在本地缓存。 NETWORK下的页面和资源从不缓存,仅在联机时可用。 如果请求的页面脱机不可用,则显示FALLBACK指定的页面。 NETWORK部分中的星号( * )确保所有其他页面和servlet仅在联机时可用。 如果缺少* ,则servlet调用将失败(甚至在线)。 清单8显示了Contact Manager的清单文件。

清单8.离线应用程序清单
CACHE MANIFEST
# Revision 1
CACHE:
default.html
list.html
scripts/core.js
scripts/localdb.js
scripts/onlinedb.js
scripts/formEvents.js
http://code.jquery.com/jquery-1.7.2.min.js
NETWORK:
*
FALLBACK:
/ offline.html

使用脱机应用程序时,重要的是要了解:

  • 脱机应用程序清单文件扩展名.appcache必须映射到text/cache-manifest多用途Internet邮件扩展名(MIME)类型。 在Apache Tomcat中,通过将mime-mapping条目添加到服务器的 web.xml文件(而不是Web应用程序的web.xml文件)中来执行此操作。 如果MIME类型不正确,大多数浏览器会默默地忽略脱机应用程序清单。
  • 如果存在脱机应用程序清单文件,则始终使用本地缓存的资源(即使在线)。
  • 仅当脱机应用程序清单文件发生更改时才更新本地资源,通常是通过更改清单文件中的注释中的修订号来进行的。 在更改应用程序清单文件之前,对HTML或CSS资源的更改不会反映在Web浏览器中。
  • 支持离线使用的任何页面都必须具有以下内容:
    <html lang="en" manifest="app.appcache">

在线或离线

使用JavaScript,您可以使用navigator.onLine布尔值检测应用程序是在线还是离线。 如果应用程序在线,则返回True。

表单事件(在线/离线处理)

在联系人管理器中,联机或脱机使用相同的表单。 使该解决方案起作用的关键在于按钮和表单事件处理程序。 检查navigator.onLine以确定要调用的操作(本地或联机)。 清单9显示了一个联系人数据加载示例。

清单9.加载数据(在HTML BODY的onLoad事件中)
if (navigator.onLine) 
{	
	// selection list needs to be populated prior to synchronizing data
	// the list is updated from the online dictionary later
	populateOfflineStates(); 
	
	setStatusText("Synchronizing contact data with server...");
	synchronizeContacts();

	setStatusText("Loading dictionary data from server...");		
	populateOnlineStates();
	
	setStatusText("Loading contact data from server...");
	loadOnlineContacts();
}
else 
{
	alert('You are currently offline.');
	populateOfflineStates();
	setStatusText("Loading contact data from local storage...");
	loadOfflineContacts();
}

数据同步

在线时,所有CRUD操作都将servlet用于创建,修改和删除操作。 联机数据库更改时,本地缓存也会更新。

脱机时,所有CRUD操作都使用本地数据提供程序来保留更改。 与服务器重新连接后:

  • 本地创建的所有记录都将保留到服务器。
  • 在本地修改的所有记录都会在服务器上更新。
  • 在本地删除的所有记录都将在服务器上删除。

清单10显示了完整的同步方法。 在同步期间,相同的联机功能用于创建,更新和删除操作。 第一步是使用jQuery $.each函数遍历本地记录。

使用isDirty属性标记在本地更新或创建的isDirty 。 如果保存操作的唯一记录ID为负(即未由MySQL数据库分配),则将其标识为新操作。 使用isDeleted属性标记在本地删除的记录。

清单10.将离线更改同步到服务器
var recordsUpdated = 0;
var recordsCreated = 0;
var recordsDeleted = 0;

$.each(data, function(i,item){
	if (item.isDeleted) {
		deleteOnlineContact(item.id, true);
		recordsDeleted++;
	}		
	else if (item.isDirty && !item.isDeleted) {
		$('input[name="contactId"]')[0].value = item.id;
		$('input[name="firstName"]')[0].value = item.firstName;
		$('input[name="lastName"]')[0].value = item.lastName;
		$('input[name="street1"]')[0].value = item.street1;
		$('input[name="street2"]')[0].value = item.street2;
		$('input[name="city"]')[0].value = item.city;
		$('select[name="state"]')[0].value = item.state;
		$('input[name="zipCode"]')[0].value = item.zipCode;
		
		var dataString = $("#editContactForm").serialize();
		postEditedContact(dataString, true);
		if (item.id > 0) {
			recordsUpdated++;
		}
		else {
			recordsCreated++;
		}			
	}
});

var msg = "Synchronization Summary\n\tRecords Updated: " + recordsUpdated + 
"\n\tRecords Created: " + recordsCreated +"\n\tRecords Deleted: " + recordsDeleted;
alert(msg);

使用getcontacts操作从数据库中获取最新数据并显示出来。 其他用户所做的任何更改都会反映出来。 然后,数据将在本地保留,以确保离线时可用。

结论

在本文中,示例应用程序展示了一种在线和离线支持的良好模式。 通过将单个HTML页面用于联机和脱机模式,并基于联机/脱机状态调用表单事件处理程序中的相应联机/脱机数据提供程序,从而维护了一致的用户体验。

数据同步算法提供了良好的基础; 它处理同步记录的脱机创建,删除和修改。 但是,它不是可用于生产的代码。 例如,当同一条记录在本地由另一个用户在服务器上修改时,它不会处理冲突。


翻译自: https://www.ibm.com/developerworks/web/library/wa-html5db/index.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值