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

时间:2014-05-13 19:54:10

标签: java android file-io filesystems

我有一个将byte[]写入磁盘的write方法。在极少数设备上,我遇到了一些奇怪的问题,写入操作成功后写入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-statement我在一些设备上比较文件长度。 一个示例输出:

After an fsync, the file's length is not equal to the number of bytes we wrote! path=/mnt/sdcard/.folder/filename, expected=233510, actual=229376 >> Original MD5 Hash: f1d298c0484672c52d9c26d04a3a21dc, written file MD5 hash: 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哈希值下失败。

1 个答案:

答案 0 :(得分:0)

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应该可以正常执行此任务。在向磁盘写入字节时,也要将流提供给哈希算法。一旦文件被写入,请将其读回并将其提供给您的哈希。比较结果。 如果您想确保数据干净,文件大小不够。