java - android 写入磁盘不可靠 - 写入 file.length !=expected.length

标签 java android file-io filesystems

我有一个写入方法,可以将 byte[] 写入磁盘。在极少数设备上,我遇到了一些奇怪的问题,即写入操作成功后写入的 file.length() != byte[].length

代码与问题

将文件写入磁盘的代码

private static boolean writeByteFile(File file, byte[] byteData) throws IOException {
    if (!file.exists()) {
        boolean fileCreated = file.createNewFile();
        if (!fileCreated) {
            return false;
        }
    }

    FileOutputStream fos = new FileOutputStream(file);
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    bos.write(byteData);
    bos.flush();
    fos.getFD().sync(); // sync to disk as recommended: http://android-developers.blogspot.com/2010/12/saving-data-safely.html
    fos.close();


    if (file.length() != byteData.length) {
        final byte[] originalMD5Hash = md.digest(byteData);

        InputStream is = new FileInputStream(file);
        BufferedInputStream bis = new BufferedInputStream(is);
        byte[] buffer = new byte[4096];

        while(bis.read(buffer) > -1) {
            md.update(buffer);
        }
        is.close();

        final byte[] writtenFileMD5Hash = md.digest();

        if(!Arrays.equals(originalMD5Hash, writtenFileMD5Hash)) {
            String message = String.format(
                    "After an fsync, the file's length is not equal to the number of bytes we wrote!\npath=%s, expected=%d, actual=%d.  >>  " +
                    "Original MD5 Hash: %s, written file MD5 hash: %s",
                    file.getAbsolutePath(), byteData.length, file.length(),
                    digestToHex(originalMD5Hash), digestToHex(writtenFileMD5Hash));
            throw new GiantWtfException(message);
        }
    }

    return true;
}

我遇到了 if 语句,我在其中比较了一些设备上的文件长度。 一个示例输出:

fsync 后,文件的长度不等于我们写入的字节数! path=/mnt/sdcard/.folder/filename, expected=233510, actual=229376 >> 原始MD5哈希:f1d298c0484672c52d9c26d04a3a21dc,写入文件MD5哈希:ab30660bd2b476d9551c15b340207a8a

我目前在 5 台设备上看到这个问题,因为我正在慢慢推出代码。一些设备数据:

问题

还有什么我可以做或改进的吗?

更多统计数据和观察结果

当前系统版本

  • 2.3.5
  • 2.3.6

型号

  • N860(LG)
  • GT-I9100G(三星)
  • GT-S5300(三星)
  • GT-S7500(三星)
  • LG-VS410PP (LG)

其他统计数据

在一般崩溃分析(来自 Crittercism)中,问题发生时总是有足够的可用磁盘空间。仍然有一些(不是全部)设备在不同的时间点围绕 no free disk space 抛出 IOExceptions。

一如既往,我从未能够在我拥有的任何测试手机上重现该问题。

假设/观察:

通常,当磁盘已满时,我会期望出现 IOException。尽管如此,我捕获的所有异常写入的字节数仍然少于应有的字节数。

有趣的是,实际写入磁盘的所有字节数都是2^15 的倍数。

编辑: 我添加了一个同样失败的 MD5 校验和验证,并稍微简化了示例代码以提高可读性。它在使用不同的 MD5 哈希值时仍然会失败。

最佳答案

philipp,file.length() 是操作系统报告的文件大小。它可能是文件在磁盘上占用的空间或文件中的字节数。

如果返回的数字是磁盘上的大小,则它与保存文件的簇数有关。例如 NTFS 通常使用 4KB 簇。如果在 NTFS 格式的卷上保存一个包含 3 个 ascii 编码字符的文本文档,则文件大小为 3 个字节,磁盘上的文件大小为 4096 字节。在具有 4KB 簇的 NTFS 上,所有文件在磁盘上都是 4096 字节的倍数。参见 http://en.wikipedia.org/wiki/Data_cluster了解更多。

如果返回的数字是以字节为单位的文件长度(来自底层文件系统的元数据)那么你应该与你写入的字节数完全匹配,尽管我不会赌我的生命.

Android 使用 YAFFS 或 EXT4,如果有帮助的话。

我强烈同意 admdrew,使用散列。 MD5 会很好用。 SHA 甚至 CRC 应该可以很好地完成这项任务。当您将字节写入磁盘时,也将流提供给您的哈希算法。写入文件后,将其读回并将其提供给您的散列器。比较结果。 如果要确保数据干净,文件大小不够。

关于java - android 写入磁盘不可靠 - 写入 file.length !=expected.length,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23640387/

相关文章:

java - 如何从 QueryDSL 检索列表/结果

java - Android:如何处理嵌入在使用 JSON 检索的字符串中的 html <img/> 标签

android - 为android中的应用程序制作小部件

java - 在 Java 中写入文件的最佳方式是什么?

java - 将导入的文本文件的行转换为数组并打印数组以在 Netbeans 和 Swing 中制作图形

java - 如何在Apache Drill中实现Mongo Storage Plugin的INNER JOINS(下推)优化?

java - 当自动缩进打开时,如何避免 Eclipse 创建 2 行?

java - 获取字符串并将其作为一个类运行

android - async{} 内部流

c - 使用函数读取文件