CLR 全面透彻解析

Silverlight 2 中的独立存储

Justin Van Patten

内容

隔离
配额
文件和目录
IsolatedStorageSettings

在 8 月 2008MSDN 杂志 》CLR 列,Andrew Pardoe Silverlight 2 中提供独立存储的简要概述 ("程序与在 CoreCLR 的 Silverlight").在本专栏,我将使用它自己的应用程序中提供有关在 Silverlight 中一起一些最佳实践的独立存储更多详细信息。

独立存储深入之前, 我首先介绍了哪种 I / O 功能有 Silverlight。Silverlight 应用程序是部分信任应用程序在 Web 浏览器在沙盒环境中运行的。在这种情况下,不允许任意访问文件系统。和充分的理由,不会希望随机、 潜在的恶意 Silverlight 应用程序您浏览 Web,才能访问您的个人文件或 wreaking 破坏系统时遇到。

但有位置很有用允许受限访问在文件系统的段的 Silverlight 应用程序是否为读取文件 (使用最终用户的同意),或在客户端上本地存储数据的有效方案。这两种方案 Silverlight 2 中支持有限的安全方式: 前者启用了该 OpenFileDialog 和后者与独立存储。

如 Cookie,独立的存储能够存储在客户端应用程序调用之间的数据。但不同 Cookie,独立的存储是一个应用程序提供能够创建、 读取、 写入、 删除,和枚举文件和虚拟文件系统中的目录下的成熟的虚拟文件系统。独立的存储可以将来维护状态和简单的应用程序的设置用于方式与 Cookie,相同,但是它也可用于在客户端上的本地保存大量数据。

独立的存储并不是新 Silverlight ; 后.NET Framework 的一部分 1.0 版。现在 Silverlight 随.NET Framework 的独立存储 API,简化子集,但它还包含新的 API 提供了一些新的概念以及的附加功能。

隔离

按可能预期独立存储的主要的方面之一就是隔离。独立的存储由许多不同的唯一存储,每个可被视为自己的虚拟文件系统组成。路径无法转义虚拟文件系统有效地隔离文件系统的其他存储区的边界。这样一个恶意应用程序能够访问该磁盘上的任意文件和目录如\..\..\..\Windows\System32—where 它可能导致损坏或访问敏感信息。

Silverlight 应用程序有权访问两个不同的存储: 用户 + 应用程序存储区 (或应用程序存储区) 是独立用户标识和应用程序标识和用户 + 网站存储 (或站点存储) 的独立用户标识和网站标识。隔离存储基于用户、 应用程序和站点的身份,则意味着应用程序仅可以访问的存储的允许访问 ; 它无法访问其他应用程序的存储。

Silverlight 使用的用户标识是相同的用户标识,基础操作系统。通过将所有独立的存储的数据存储在当前用户的本地应用程序数据目录中,每个用户隔离 Silverlight。图 1 具有更多详细信息独立的存储所在的位置为每个操作系统。

图 1 位置独立存储的文件系统中
操作系统 在文件系统中的位置
Windows Vista <systemdrive> \Users\ <user> \AppData\LocalLow\Microsoft\Silverlight\is
Windows XP <systemdrive> \ Documents and Settings\ <user> \Local Settings\Application Data\Microsoft\Silverlight\is
Mac OS X /Users/ <user> / 库 / 应用程序支持 / Microsoft / Silverlight / 是

在应用程序存储是唯一的商店,每个应用程序。Silverlight 应用程序只能访问其应用程序存储区 ; 它不能访问任何其他应用程序的应用程序存储区。在应用程序存储基于用户的标识和应用程序的标识。Silverlight 应用程序的标识是 Silverlight 应用程序的 XAP 文件完整 URL。是例如 https://Microsoft.com/App.xap 是驻留在 https://Microsoft.com/App.xap Silverlight 应用程序的应用程序标识。应用程序标识是区分大小写,因此 https://MICROSOFT.COM/App.XAPhttps://Microsoft.com/App.xap 有相同的标识。在应用程序存储并不是新 Silverlight ; v 2.0 后已存在.NET Framework 中。下面的代码演示如何创建应用程序的应用程序存储区中的文件:

try {
    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
    using (var stream = store.CreateFile("hello.txt"))
    using (var writer = new StreamWriter(stream)) {
        writer.Write("Hello World");
    }
}
catch (IsolatedStorageException) {
    // Isolated storage not enabled or an error occurred
}

Silverlight 独立存储的最佳实践

使用独立的存储时, 请按照这些指南将帮助您避免的问题,并最多的保护独立存储的生成提供。

  • 包装 Try/Catch 块中的独立存储所有调用为复原潜在 IsolatedStorageExceptions,如果独立存储可能引发的已禁用,或已被删除存储区。
  • 如果您的 Silverlight 应用程序需要独立存储中存储大量数据,请考虑,以便它不会影响在网站上的其他应用程序和其他应用程序不会影响它自己的站点上承载它。
  • 如果有需要共享数据在客户端上的 Silverlight 应用程序的一组,请在相同站点上承载它们。
  • 保持独立的存储路径尽可能以防止达到 260 个字符限制内部的完整路径。
  • 加密存储在独立存储的敏感数据。
  • 使用 IsolatedStorageSettings 存储独立存储中的对象和简单的设置。
  • 如果您希望使用文件和基于流的 API 使用 IsolatedStorageFile 存储大量数据,或者需要对独立存储的内容的精细控制。

独立的存储此处使用的 API 都位于 System.IO.IsolatedStorage 命名空间。 您可以获取应用程序存储通过调用 IsolatedStorageFile.GetUserStoreForApplication 的实例。 如果启用了独立的存储 (它是默认情况下安装 Silverlight 时),这将返回可用于使用存储的 IsolatedStorageFile 实例。 如果最终用户已禁用独立的存储,则将引发一个 IsolatedStorageException。

最终用户可以启用 / 禁用独立的存储区因此很重要始终包装调用 IsolatedStorageFile.GetUserStoreForApplication try/catch 块内。 实际上,IsolatedStorageException 可能会引发任何独立的存储操作中。 如果最终用户删除独立的存储应用程序在使用它 (下一操作将导致一个 IsolatedStorageException) 时,则可能发生此情况。 此外,如果应用程序的多个实例都运行 (在多个浏览器窗口或选项卡例如) 和的存储,然后下一个独立的存储操作执行的其中一个其他应用程序实例,实例删除一个将引发一个 IsolatedStorageException。

最佳的做法是换行以防止 IsolatedStorageExceptions 所有调用 try/catch 块内的独立存储。 我已封装在 try/catch 块说明这,但简化操作此第一个代码示例,代码示例的其余部分将不显示 try/catch 块。 请考虑所有剩余的代码示例为正在被隐式封装 try/catch 块中。 (有关最佳实践的一个摘要,请参阅侧栏"Silverlight 独立的存储 Best Practices")。

既然您已了解存储应用程序,让我们看看网站存储。 在网站存储是的所有 Silverlight 应用程序在相同站点都有权访问的共享的存储。 这允许共享它们之间的数据来自相同场所的应用程序。

Microsoft Office 2007 跨应用程序的组具有公共的首选项相同,您可以想像开发一套的全部位于同一站点上和共享常见的一组存储在客户端上的首选项的 Silverlight 应用程序。 网站存储基于用户的标识和站点的标识。 站点标识一个 Silverlight 应用程序类似于应用程序标识,但它只包括方案、 主机与不带 XAP 文件路径的该 URL 的端口。 如将应用程序标识网站标识也是区分大小写。 在共享的网站存储是 Silverlight 中的新,并且不在.NET Framework 中可用。

IsolatedStorageFile.GetUserStoreForSite 可用于获取网站存储的实例。 下面的代码在操作中演示此操作:

using (var store = IsolatedStorageFile.GetUserStoreForSite())
using (var stream = store.CreateFile("sharedhello.txt"))
using (var writer = new StreamWriter(stream)) {
    writer.Write ("Hello Shared World");
}

另一个应用程序使用相同的站点标识未能读取共享的文件使用以下代码:

using (var store = IsolatedStorageFile.GetUserStoreForSite()) {
    if (store.FileExists("sharedhello.txt")) {
        using (var stream = store.OpenFile("sharedhello.txt", FileMode.Open))
        using (var reader = new StreamReader(stream)) {
            string contents = reader.ReadToEnd(); // "Hello Shared World"
        }
    }
}

若要更好地了解如何将给定的 URL 为 Silverlight 应用程序映射到一个应用程序标识和网站标识,请参阅 图 2 。 正如您所看到,即使前四个 URL 是不同,它们都将映射到相同的应用程序标识和网站标识。 这只有因为,在事实标识不区分大小写、 查询字符串不包含,并且端口 80 是默认端口为 HTTP 方案。 但是,如果指定另一个端口,如 8080,然后应用程序的标识被视为不同。 如果省略前导"www"主机,应用程序标识被视为不同,因为主机不同。

图 2 Silverlight 应用程序 URL 以及关联的应用程序和网站标识
应用程序 URL 应用程序标识 网站标识
https://www.microsoft.com/App.xap HTTP://WWW.MICROSOFT.COM/APP.XAP HTTP://WWW.MICROSOFT.COM
https://www.MICROSOFT.COM/App.XAP HTTP://WWW.MICROSOFT.COM/APP.XAP HTTP://WWW.MICROSOFT.COM
https://www.microsoft.com/App.xap?foo=bar HTTP://WWW.MICROSOFT.COM/APP.XAP HTTP://WWW.MICROSOFT.COM
https://www.microsoft.com:80/App.xap HTTP://WWW.MICROSOFT.COM/APP.XAP HTTP://WWW.MICROSOFT.COM
https://www.microsoft.com:8080/App.xap HTTP://WWW.MICROSOFT.COM:8080/APP.XAP HTTP://WWW.MICROSOFT.COM:8080
https://Microsoft.com/App.xap HTTP://MICROSOFT.COM/APP.XAP HTTP://MICROSOFT.COM
https://www.microsoft.com/App.xap HTTPS://WWW.MICROSOFT.COM/APP.XAP HTTPS://WWW.MICROSOFT.COM
https://www.microsoft.com/demo.xap HTTP://WWW.MICROSOFT.COM/DEMO.XAP HTTP://WWW.MICROSOFT.COM

该方案是其他 (HTTPS 而不是 HTTP) 安全的 HTTPS URL 也将产生不同的标识。 图 2 中列出的最后一个 URL 将表中, 有一个不同的应用程序标识,与前四个 URL 但这意味着这些两个应用程序可以选择在其共享的网站存储库中存储数据相互数据共享客户端上在相同站点标识。

fig03.gif

图 3 独立的存储配额组

配额

配额是可供应用程序的独立的存储空间量有限的。 这包括存储在独立的存储,以及一些额外的开销为每个目录中的数据和创建 1 (KB 每个) 的新文件。

与在.NET Framework 不同 Silverlight 使用一个概念,称为管理独立存储中的配额的配额组。 配额组是一组共享同一个配额的独立存储。 在 Silverlight 中, 存储按分组站点,以便具有相同的站点标识的所有应用程序共享同一个独立的存储配额。 是例如驻留在 Microsoft.com 上的所有应用程序共享同一配额。

独立的存储可以有零个或多个配额组。 图 3 显示了两个假想的配额组: 一个用于 microsoft.com,另一个用于 blogs.microsoft.com。 在大多数的一个共享的网站存储和零个或多个应用程序存储具有每个配额组。

情况默认,每个配额组分配 1MB 的默认的配额。 这应该是足够的空间存储独立存储中的简单设置和文本文件的应用程序。 但是,因为配额从相同场所的所有应用程序之间共享的配额可以轻松地访问如果只有一个在应用程序使用大量的空间。 如果一个站点承载多个 Silverlight 应用程序,并且每个应用程序将数据存储在独立存储可用空间量可以快速降低。 此外,如果应用程序需要将大量数据保存在客户端上,1MB 的可用空间不会始终为足够。

如果应用程序需要独立存储中的更多空间,请解决此,它可以请求更大的配额。 当应用程序请求更大的配额时,Silverlight 将提示最终用户若要允许或拒绝该请求。 下面的代码说明如何请求更大的配额:

using (var store = IsolatedStorageFile.GetUserStoreForApplication()) {
    // 5 MB of isolated storage space is needed
    int spaceNeeded = 1024 * 1024 * 5;

    if (store.AvailableFreeSpace < spaceNeeded) {
        if (store.IncreaseQuotaTo(store.Quota + spaceNeeded)) {
            // The user accepted the request
        }
    }
}

在此代码,使用 AvailableFreeSpace 属性来确定可用独立存储中多少可用的空间。 该代码知道它需要 5MB 的可用空间。 如果 5MB 没有,它通过调用其传递所请求的配额大小以字节为单位) 的 IncreaseQuotaTo 请求更大的配额 (此案例当前配额以及的需要的额外的空间)。

在调用 IncreaseQuota 必须进行从一个用户启动事件 (如) 在按钮单击事件处理程序。 如果未调用从用户启动事件,Silverlight 将不提示用户并请求将被拒绝 (该方法将返回 False)。 如果在调用一个用户启动的事件和最终用户允许该请求,IncreaseQuotaTo 返回 True 并请求的大小会增大组的配额。

由于配额是每个在 Silverlight 中的站点,您可能需要来承载 Silverlight 应用程序不同根据站点和应用程序的需要。 是例如假设您要创建一个 Silverlight 视频播放机,独立存储中保存用户的前 10 个收藏夹视频。 每个视频可达 10MB,这意味着应用程序需要增加为配额超过 100 MB。 主网站 (是例如 contoso.com/Player.xap) 可能会工作正常如果 Player.xap 唯一 Silverlight 应用程序位于网站上,可以承载该应用程序。 但如果有 contoso.com 上的其他许多 Silverlight 应用程序,它们将所有被竞争的独立存储中的共享空间。

要解决此问题,您可能需要考虑宿主 (如 Player.contoso.com/Player.xap) 自己子域上视频的播放机应用程序。 同样,如果您计划创建的需要共享数据的 Silverlight 应用程序套件,您需要承载所有这些相同的站点上,并确保它们和协同工作。

Silverlight 使用配额组使最终用户可以管理他们的计算机上的独立存储的使用更容易。 而不是在显示不同的 Silverlight XAP 文件的 URL 洗衣列表,最终用户将显示一个使用独立的存储的网站列表。 这种方式,最终用户可以快速确定的网站,其信任和哪些网站他们不信任其计算机上存储数据。

向最终用户独立的存储被称为应用程序存储。 最终用户可以管理独立的存储使用 Silverlight 配置对话框。 若要打开对话框,右键单击任何在 Web 浏览器中运行的 Silverlight 应用程序,然后单击弹出菜单中的 Silverlight 配置。 然后单击应用程序存储选项卡在 Silverlight 配置对话框中。 此选项卡列出当前为每个使用与当前使用的大小和配额的独立的存储在 Web 网站。 此列表是实质上是配额组的列表。 从此处,最终用户可以删除组或启用和禁用独立的存储。

文件和目录

创建独立存储中的目录和文件是简单的。 请看一下 图 4 。 此处可以看到用来创建目录的 CreateDirectory。 您可以创建顶级目录以及子目录。 若要获取存储区中的目录的列表,请使用 GetDirectoryNames。

图 4 创建目录和文件

using (var store = IsolatedStorageFile.GetUserStoreForApplication()) {
    store.CreateDirectory(@"dir1");
    store.CreateDirectory(@"dir1\subdir1");
    store.CreateDirectory(@"dir1\subdir2");
    store.CreateDirectory(@"dir2");

    string[] topLevelDirs = store.GetDirectoryNames(); // { "dir1", "dir2" }
    string[] dir1SubDirs = store.GetDirectoryNames(@"dir1\*"); 
    // { "subdir1", "subdir2" }

    store.CreateFile(@"toplevel.txt");
    store.CreateFile(@"dir1\subdir1\subfile.txt");

    string[] files = store.GetFileNames(); // { "toplevel.txt" }
    string[] subfiles = store.GetFileNames(@"dir1\subdir1\*");
    // { "subfile.txt" }
}

也是接受 searchPattern 支持单字符的字符串的 GetDirectoryNames 的重载 ("?") 和 multi-character ("*") 通配符。 注意如何一个 multi-character ("*") 通配符用来获取"dir1"中所包含的目录。 创建文件是类似于创建目录: 在存储或内部目录的根目录中创建文件。

在独立存储中创建文件和目录时, 请相对独立的存储路径被扩展为完整的系统路径内部的记住 (Silverlight 应用程序不见这些完整的路径)。 完整的路径不能超过 260 个的字符,Windows 和 Mac。 因此尽管您在独立存储内的相对路径可能只是 10 个字符,这些 10 个字符将转换到更长路径时追加到根系统路径到独立的存储文件系统上的位置。 如果生成系统路径最终被超过 260 个字符时,将引发异常。

独立存储的位置取决于当前用户和操作系统。 图 1 中上一步您看到了独立的存储 Windows Vista、 Windows XP 和 Mac OS X 上的位置。

Microsoft\Silverlight\is 目录包含内部的独立的存储结构。 这是在配额组、 存储、 记帐的数据和等等信息存储,在实际的数据一起的 Silverlight。 文件和在此目录的目录的细节不重要 (这些是我们保留在未来版本中更改的权利的实现细节,),但一定要注意目录结构不会为完整路径的长度添加额外的开销。

例如,假设 Silverlight 应用程序已创建了一个名为 foo.txt 的应用程序存储区的根目录中的文件。 Windows Vista 上完全 obfuscated 的系统文件的路径将类似此:

C:\Users\<user>\AppData\LocalLow\Microsoft\Silverlight\is\plnvdo2y.zmg\
01ftptzg.h3o\1\s\4onbrsocvqffhjl0kn0sfxcidggvvqzoyl1to4ulrpif1vkwyaaahaa\f\foo.txt

在 Windows XP 上它看上去如下:

C:\Documents and Settings\<user>\Local Settings\Application
Data\Microsoft\Silverlight\is\plnvdo2y.zmg\01ftptzg.h3o\1\s\
4onbrsodcvqffhjl0kn0sfxcidggvvqzoylto4ulrpif1vkwyaaahaa\f\foo.txt

并 Mac OS X 上它看上去如下:

/Users/<user>/Library/Application
Support/Microsoft/Silverlight/is/plnvdo2y.zmg/01ftptzg.h3o/1/s/
4onbrsodcvqffhjl0kn0sfxcidggvvqzyl1to4ulrpif1vkwyaaahaa/f/foo.txt

假定 <user> 10 个字符,完整路径长度为 foo.txt 是在 Windows Vista,Windows XP 上 190 字符 158 字符并且 167 字符 Mac OS X 上。 一般来说考虑一下,独立的存储路径被限制为 Windows Vista 上的 109 字符、 Windows XP 上的是 77 字符和假定当前用户的用户名为 10 个字符长度的 Mac OS X 上的 100 个字符。

若要是安全,最好的做法是保留独立的存储路径尽可能以防止达到 260 个字符限制的完整路径。

有趣一点注意有关 图 1 中列出的位置是独立存储的事实不浏览器的 Internet 临时文件的一部分。 这意味着独立的存储共享跨所有受支持的浏览器并时您 (或您的用户要精确) 清除临时 Internet 文件,独立存储的内容不会删除。

此行为是设计,因为应用程序可以与其他首选项和用户可能不需要删除与临时 Internet 文件设置的独立存储中存储创建用户的数据 (如文档和照片)。 若要删除独立存储的内容,请使用 Silverlight 配置对话框如前面所述。

一件事来注意虽然独立存储的位置进行模糊处理和存储都彼此隔离,数据存储在独立存储中不受保护 (这就是未加密)。 这意味着在计算机上运行的其他 (非 Silverlight) 应用程序可能能访问数据。 如果您需要在独立存储中存储敏感数据,加密数据之前将其存储 (有加密 API 在 Silverlight 中可用,请参阅 System.Security.Cryptography MSDN 库)。

IsolatedStorageSettings

Silverlight 包含一个新的方便类,称为 IsolatedStorage­settings 用于快速而轻松地存储在独立存储中的对象。 IsolatedStorageSettings 是支持保存应用程序存储区或网站存储中的键 / 值对的字典集合。 它自动负责序列化对象,并将其保存在独立存储中。 已序列化的对象将保存到名为 __LocalSettings 的独立存储中的文件中。

图 5 中的代码显示如何使用将一个简单的对象保存在应用程序存储中的 IsolatedStorageSettings。 我定义名 MySettings 为具有三个属性的简单类的下面: 宽度、 高度和颜色。 然后,我创建 MySettings 的实例并填充某些值。 将下一步,我添加到 IsolatedStorageSettings.ApplicationSettings 设置项下。

最后我调用保存的序列化对象并将序列化的数据保存到应用程序存储区。 检索对象恰好是同样容易:

MySettings settings;
if (!IsolatedStorageSettings.ApplicationSettings.TryGetValue("settings",
    out settings)) {
    // Settings was not previously stored in isolated storage
    // create a new object initialized with default values
    settings = new MySettings {
        Width = 100,
        Height = 100,
        Color = Colors.Orange
    };
}

此代码使用 TryGetValue 方法以避免异常 (如果该项未找到 (表示不已以前保存数据)。 图 5 中的代码保存到应用程序存储区的对象。 要将对象保存到网站存储中,可而不是 IsolatedStorageSettings.ApplicationSettings 使用 IsolatedStorageSettings.SiteSettings。

图 5 将对象保存到应用程序存储区

public class MySettings {
    public int Width { get; set; }
    public int Height { get; set; }
    public Color Color { get; set; }
}
var settings = new MySettings {
    Width = 200,
    Height = 300,
    Color = Colors.Blue
};

IsolatedStorageSettings.ApplicationSettings["settings"] = settings;
IsolatedStorageSettings.ApplicationSettings.Save();

如果您只需要在独立存储中存储简单对象或设置,则使用 IsolatedStorageSettings 可以使大量有用。 但如果需要更细致地控制存储在独立存储中的数据的格式或存储大量数据,您将可能需要使用文件和基于流的独立的存储 API。

有关详细信息在 Silverlight 中的独立存储请参阅 领先在 2 月 2009.

将您的问题和提出的意见发送至 clrinout@Microsoft.com.

Justin van Patten 是 Microsoft CLR 团队在项目经理,他在其中工作的文章类库。 他可以访问通过在 BCL 团队博客 blogs.msdn.com/bclteam.