FileConnection的API简介

1 引言 本文档讲述了 FileConnection API [JSR-075] ,并简要介绍了该包中包含的 MIDlet 范例以及诺基亚特有的一些实现细节。本文档假定读者熟悉 Java 编程,并具有移动信息设备描述 (Mobile Information Device Profile MIDP) 编程的基础, MIDP 编程基础可参见诺基亚论坛中的文档 MIDP 1.0: Introduction to MIDlet Programming [MIDPPROG] FileConnection API 是一个受限 API ,例如,它具有安全上的限制。因此,读者还必须熟悉 MIDP 2.0 安全架构的概念;诺基亚论坛中的文档 MIDP 2.0: Tutorial On Signed MIDlets [SIGNMID] 介绍了安全模型和签名过程。
FileConnection API JSR-75: PDA Optional Packages for the J2ME ™ Platform 中定义, JSR-75 包含两个 Java ™ 2 Platform, Micro Edition (J2ME™) 可选包,用于支持 PDA 之类的设备的功能。可选包提供了对个人信息管理 (PIM API) 数据库和本地文件系统 (FileConnection API) 的访问。这两个包相互之间完全独立,因此,设备可以包含其中任意一个包,也可以同时包含两个包。
 
         2 FileConnection API
2.1 简介 在 J2ME 设备中,可以利用通用连接架构 (Generic Connection Framework GCF) ,通过各种连接类型特有的 Connection 接口实现,来处理 I/O 操作。构建不同的 Connection 扩展要使用适合于不同连接类型的 URL ,如 http://sockets:// 等。原则上讲,GCF通常足以支持文件连接,但是,GCF不是J2MEMIDP的必选项,大多数实现中都没有包含GCF。即使构建了此类的连接,仍可能不支持文件操作,如重命名文件或删除文件。此外,对本地文件的访问关系到安全、私密和系统稳定性等方面的重要问题,在实现时必须对此给予考虑。
FileConnection API [JSR-075] 通过提供对文件系统的访问以及对文件操作的支持,弥补了上述缺陷。该 API 假定设备中存在一个可定位的文件系统,如可移动的内存卡、闪存或其它类型的永久存储器。该 API 并不是记录管理系统 (Record Management System RMS) 的替代物,它只是对 RMS 的补充,从而实现 MIDlet 与本地应用软件的交互。例如, MIDlet 可以访问并处理本地应用软件先前利用内嵌数码照相机拍下的图象。一般情况下,这些图象存储在设备内存中,通过 FileConnection API ,可以实现 CLDC/CDC 应用软件对它们的访问。
API 的最低要求是 CLDC 1.0 ,因此,即使没有用户界面的基本 J2ME 设备也能实现它。但是,本文档和 MIDlet 范例都假定 FileConnection API 是在 MIDP 2.0 设备上实现的。此外,该 API 的安全机制还在 MIDP 2.0 安全架构 [SIGNMIDP] 下。
由于 FileConnection API 是可选的扩展项,所以,增加了一个系统属性用于表明该 API 是否存在。系统属性 microedition.io.file.FileConnection.version 包含实现的 API 版本。目前,该属性的值为 1.0 null 1.0 表明该 API 的当前状态, null 则说明未使用该 API 。另外一个有用的系统属性是 file.separator ,它包含用于隔离目录的字符,其典型值为 /
API 非常简单,仅包含一个类、两个接口和两个异常。其中 FileConnection 接口是最为重要的部分,它扩展了 Connection 接口,提供对目录和单个文件的访问。创建 FileConnection 的实现需要使用方法 Connector.open() 。方法 open() 的参数是一个 URL ,如 RFC 1738 [RFC 1738] RFC 2396 [RFC 2396] 中的定义, URL 的格式为 file:// / ,其中, host 通常为空, path 以文件系统的根目录开始,并往下扩展到一个特定文件或目录。 Symbian 设备中,典型的文件 URL 范例如下所示:
file:///C:/Nokia/Images/Image(001).jpg
文件系统的根目录因设备而异,由于根目录是由设备的操作系统从逻辑上进行定义的,故它们没有必要与物理上的内存单元对应。此外,某些诺基亚设备支持虚拟目录,虚拟目录实际上就是指向某个目录的链接。例如,在内存卡中,拍下的图象可能位于根目录 e: 下的路径 file:///e:/Nokia/Images 中,此外,还有一个虚拟根目录 Images/ 指向实际的物理位置。假设 MIDlet 具有访问目录 Images/ 的权限,但却无权访问根目录 e:/ ,这种情况下,虚拟根目录将有助于更容易地定位,并可简化安全许可。
FileSystemRegistry 提供了方法 listRoots() ,该方法的返回值是文件系统中根目录的枚举,其中包括逻辑根目录和虚拟根目录。该 API 还考虑到了某些设备在运行期间具有添加或删除文件系统的能力。类 FileSystemRegistry 提供注册 FileSystemListener 监听器的方法,在修改设备中的根目录时,将调用该方法。建议每个应用软件都注册一个 FileSystemListener 监听器,在发生变化时,监听器将被告之发生了变化并做出适当响应。
由于 FileConnection 接口能够扩展 Connection ,并可利用 GFC 创建对象, FileConnection 与其它常见 Connection 实现之间存在着一些显著差异。其中一个最显著的差异是,即使当前文件不存在,也能成功调用 Connector.open() 。这在创建新文件或新目录时是很有必要的。但是,打开不存在文件的 InputStream 是非法的。
 
另一个差异是,在关闭输入或输出流后, FileConnection 仍能保持打开状态。因此,在访问文件后调用方法 FileConnction.close() 是很重要的,这样做可以保证其它应用能访问该文件。相应地,利用 OutputStream 对文件做出的修改也不会立即对文件系统可见。这取决于实际的实现以及设备的操作系统。方法 flush() 可以保证缓冲区能够被清空,并且其中的内容可以写入实际文件中。
与其它 Connection 对象的另外一个差别是,通过方法 setFileConnection() ,可以实现 FileConnection 对象的重用。该方法主要用于目录转换。其思想是,如果在特定目录中构建了 FileConnection ,则可以调用方法 list() 获得该目录的子文件和子目录的枚举。该枚举值的成员可作为参数被传递给 setFileConnection() ,之后,原始 FileConnection 就指向了这个特定的子文件或子目录。通常来说, setFileConnection() 的参数是已存在的其它子文件或目录的相对路径,或者是表示上层目录的 .. 参数。
另外一个针对所有 I/O 操作的常见注意事项是,必须在异于 GUI 线程的其它线程中执行 I/O 操作。在使用 FileConnection API 时,这个建议同样适用。由于安全架构的原因,与文件相关的操作可能会发生用户提示,要求用户对操作进行确认,考虑到这一点,在异于 GUI 线程的其它线程中执行 I/O 操作尤为重要。如果在 GUI 线程中执行 I/O 操作,并且需要使用用户提示,则 MIDlet 就有可能会死锁。
         2.2 安全
在用 FileConnection API 开发应用软件时,考虑 API 的安全隐患是非常重要的。为了保护用户的个人数据和整个系统的安全,文件操作是受限制的。只有在获得必须的许可后,才能执行文件操作;否则,将抛出 SecurityException 异常。因此在适当的时候使用捕获 SecurityException 的语句非常重要。
MIDP 2.0 MIDlet 既可以是不可信的,也可以是可信的 [SIGNMID] 。在第一种情况下,设备无法确知 MIDlet 的由来和完整性,因此,在没有显式的用户许可时,不允许调用受限的 API 。也就是说,如果需要访问一个文件或目录,将会显示用户提示,而用户必须显式地确认该操作。
MIDlet 是可信的情况下,设备可以通过 X.509 证书判断 MIDlet 的由来和完整性。这些 MIDlet 可以根据安装时的安全域设置,自动地获得许可。此外, MIDlet 需要在 Java 应用描述符 (Java Application Descriptor JAD) 文件中的属性 MIDlet-Permission 中包含文件操作许可。
共定义了下面两个有关 FileConnection API 的许可:
javax.microedition.io.Connector.file.read
javax.microedition.io.Connector.file.write
如果希望以 READ 模式打开文件,并获取文件的输入流,则第一个许可是必需的。在用类 FileSystemRegistry 注册监听器时,也需要第一个许可。如果希望以 WRITE 模式打开文件,并打开文件的输出流,则第二个许可是必需的。此外,诸如删除、修改目录之类的操作也需要写入许可。如果以 READ_WRITE 模式打开文件,则同时需要两个许可。这些许可包含在 Read User Data Access Write User Data Access 功能组中。
对许可的授权或否认取决于 MIDlet 安装到的安全域。某些安全域可以完全地授权许可,而其它域则可能仅在得到显式用户同意的情况下才允许授权。实现时,可以对每个域允许的许可进行定义。但是,仍希望第三方和不可信域按照表 1 的方式定义许可模式:
 

功能组
可信的第三方域
不可信的域
默认设置
允许的设置
默认设置
允许的设置
Read User Data
Oneshot
Session, Blanket,
Oneshot
Oneshot, No

 
 

Access
Oneshot, No
Write User Data Access
Oneshot
Session, Blanket, Oneshot, No
No
Oneshot, No

1 :允许的和默认的许可模式
1 实际上说明,每次创建文件或目录连接时,不可信的 MIDlet 总会弹出一个提示。此外,如果以 READ_WRITE 模式打开连接,将会出现两个提示,分别对应于两个许可。可信的第三方 MIDlet 的情况与此类似,但是用户可以手动地把该设置改为 session ,这样,在运行 MIDlet 时,用户就会仅被询问一次。值得注意的另外一点是,许可是以基于文件到文件的方式给出的。也就是说,用户在访问每个文件或目录时都会被提示。该范例中的 MIDlet 需要在文件系统中遍历,因此会出现多个用户提示,对于此类的 MIDlet 来说,上述情况尤其值得注意。这种情况能够有力地说明,在使用受限 API 时,为什么应该对 MIDlet 签名。
此外,关于文件访问还有另外一个层面上的局限。根据安装时赋给 MIDlet 的安全域, MIDlet 将能够访问文件系统的一个子集。这种设计可以保护用户数据,并可防止对操作系统的损害。特别地,可信的第三方 和 不可信域中的 MIDlet 仅能访问一组被指定的公共目录(其中包括存放图象、视频、公共文件的目录)以及每个 MIDlet 的专用目录。这是推荐使用虚拟目录的原因之一,因为,有可能允许访问根目录 Images/ ,但可能由于 MIDlet 无法访问 e: ,从而造成无法从 e:/ 切换到目录 e:/Nokia/Images/
某些文件相关操作会检查是否获得了适当的安全许可,但是,在调用方法 Connector.open() 时,开发人员需要给予特别考虑。在创建 FileConnection 和授权适当的许可后,对于需要同样许可的其它操作来说,该许可仍然有效。例如,一旦为写操作创建 FileConnection ,则调用删除( delete )操作也已经被授权。如果创建的 FileConnection 仅具有对读操作的许可,则在调用方法 delete() 时,将会要求对写操作的许可,在必要时,还将出现用户提示。
方法 setFileConnection() 还将根据原始 FileConnection 的创建模式检查文件许可。这是非常合理的,因为 setFileConnection 能够改变当前连接,以便指向不同的文件或目录。
         2.3 诺基亚特有的目录
在许多诺基亚设备中,一些目录是针对特定任务而设计的。例如,照相机设备在特定的 图像 目录下存储拍下的照片。为了使开发人员更加容易地访问此类目录,实现了 FileConnection API 的诺基亚设备包含额外的系统属性,以用于定位这些目录。
并不是所有设备都需要这些属性,因此,不要想当然地认为它们存在。开发人员应该注意,如果属性值为 null ,则需寻找一个替代方法。
2 列举了系统属性。第一列是属性名,它以 URL 的格式指向特定目录。该 URL 可以被直接传递给 Connection.open() 。第二列是一个额外的属性,它包含该目录的本地化名称。建议用第二列中的属性代替目录的通用的非本地化名称,以保持 MIDlet 与其它设备 UI 的兼容性。
 
 

属性
局部属性
描述
fileconn.dir.photos
fileconn.dir.photos.name
该属性指向的目录存储集成照相机拍下的相片或其它图象。
fileconn.dir.videos
fileconn.dir.videos.name
与上面类似,但是存储的内容是视频。默认情况下,下载的视频也保存在该目录下。
fileconn.dir.tones
fileconn.dir.tones.name
铃声或其它类似的音频文件存储在该目录下。
fileconn.dir.memorycard
fileconn.dir.memorycard.name
在内存卡可用的情况下,该属性指向内存卡的根目录。
fileconn.dir.private
fileconn.dir.private.name
MIDlet 套件的专用工作目录。

2 :诺基亚特有的目录
FC api是JSR 75, PDA Optional Packages for the J2ME Platform的一部分,用于访问本地文件系统。


FC api通过Generic Connection Framework(GCF)访问文件系统,允许访问包括存储卡在内的文件系统。


包括如下两个接口和三个类:
FileConnection 访问文件和文件夹的接口。
FileSystemListener 添加删除根目录文件系统的状态监听的接口。
FileSystemRegistry 添加删除根目录文件系统的接口注册类。 


ConnectionClosedException 当一个文件句柄的操作被调用,而文件已经被关闭时抛出的异常。 
IllegalModeException 当操作所对应的模式不被文件打开模式支持时抛出的异常。

[code]
// 判断是否支持FC:
if(System.getProperty("microedition.io.file.FileConnection.version") != null){ // file.separator
    // FCOP available
} else {
    // FCOP not available
}

// 打开文件:
// CFCard/:
FileConnection fc = (FileConnection) Connector.open("file:///CFCard/"); 
// SDCard/:
FileConnection fc = (FileConnection) Connector.open("file:///SDCard/"); 
// MemoryStick/:
FileConnection fc = (FileConnection) Connector.open("file:///MemoryStick/"); 
// C:/:
FileConnection fc = (FileConnection) Connector.open("file:///C:/"); 
// / File:
Connection fc = (FileConnection) Connector.open("file:"); 

// 只读方式打开一个文件:
String url = "file:///data.txt";
InputConnection conn = null;
int mode = Connector.READ_ONLY; 
  
try {
    conn =(InputConnection) Connector.open( url, mode );
    // Always check whether the file or directory exists.
    // Create the file if it doesn't exist.
    if(!conn.exists()) {
    }
} catch( IOException ioe ){
    // no file
}

// 创建一个文件:
String url = "file:///SDCard/data.txt";
FileConnection conn = null;
int mode = Connector.WRITE_ONLY;
try {
    conn = (FileConnection)Connector.open(url, mode);
    if(filecon.create()){ // create the file
       OutputStream out = conn.openOutputStream();
       // now write data to the file
    }
    conn.close();
} catch(IOException e){
    // error
} catch(SecurityException e){
    // no permission to create/write
}

// 列举一个目录下的文件:
// FileConnection.list(String filter, boolean includeHidden)
String url = "file:///C:/";
FileConnection conn = null;
try {
    conn = (FileConnection) Connector.open(url);
    if( conn.isDirectory() ) {
        Enumeration names = conn.list();
        while( names.hasMoreElements() ){
            String name = (String) e.nextElement();
            // do something
        }
    } else {
        // not a directory!
    }
} catch(IOException e) {
    // could not access the URL
} catch(SecurityException e) {
    // no permission to read the directory
}

// 读取文件内容:
String url = "file:///CFCard/data.txt";
InputConnection conn = null;
int mode = Connector.READ_ONLY; 

try {
    FileConnection fc = (FileConnection)Connector.open(url, mode);
    if(!fc.exists()) {
        throw new IOException("File does not exist");
    }
    InputStream is = fc.openInputStream();
    byte b[] = new byte[1024];
    int length = is.read(b, 0, 1024);
    System.out.println("Content of "+fileName + ": "+ new String(b, 0, length));
} catch (Exception e) {
}[/code]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值