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

phi*_*ipp 5 java filesystems file-io android

我有一个写入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;
}
Run Code Online (Sandbox Code Playgroud)

我正在进入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哈希值下失败.

And*_*eas 0

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

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

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

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

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