首页 > 解决方案 > Copied DocumentFile has different siize and hash to original

问题描述

I'm attempting to copy / duplicate a DocumentFile in an Android application, but upon inspecting the created duplicate, it does not appear to be exactly the same as the original (which is causing a problem, because I need to do an MD5 check on both files the next time a copy is called, so as to avoid overwriting the same files).

The process is as follows:

  1. User selects a file from a ACTION_OPEN_DOCUMENT_TREE
  2. Source file's type is obtained
  3. New DocumentFile in target location is initialised
  4. Contents of first file is duplicated into second file

The initial stages are done with the following code:

// Get the source file's type
String sourceFileType = MimeTypeMap.getSingleton().getExtensionFromMimeType(contextRef.getContentResolver().getType(file.getUri()));

// Create the new (empty) file
DocumentFile newFile = targetLocation.createFile(sourceFileType, file.getName());

// Copy the file
CopyBufferedFile(new BufferedInputStream(contextRef.getContentResolver().openInputStream(file.getUri())), new BufferedOutputStream(contextRef.getContentResolver().openOutputStream(newFile.getUri())));

The main copy process is done using the following snippet:

    void CopyBufferedFile(BufferedInputStream bufferedInputStream, BufferedOutputStream bufferedOutputStream)
    {
        // Duplicate the contents of the temporary local File to the DocumentFile
        try
        {
            byte[] buf = new byte[1024];
            bufferedInputStream.read(buf);

            do
            {
                bufferedOutputStream.write(buf);
            }
            while(bufferedInputStream.read(buf) != -1);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            try
            {
                if (bufferedInputStream != null) bufferedInputStream.close();
                if (bufferedOutputStream != null) bufferedOutputStream.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }

The problem that I'm facing, is that although the file copies successfully and is usable (it's a picture of a cat, and it's still a picture of a cat in the destination), it is slightly different.

  1. The file size has changed from 2261840 to 2262016 (+176)
  2. The MD5 hash has changed completely

Is there something wrong with my copying code that is causing the file to change slightly?

Thanks in advance.

标签: javaandroidfiledocumentfile

解决方案


您的复制代码不正确。它假设(错误地)每次调用都read将返回buffer.lengthbytes 或 return -1

应该做的是捕获每次在变量中读取的字节数,然后准确写入该字节数。您关闭流的代码很冗长,而且(理论上1)也有问题。

这是一个重写,解决了这两个问题,以及其他一些问题。

void copyBufferedFile(BufferedInputStream bufferedInputStream,
                      BufferedOutputStream bufferedOutputStream)
         throws IOException 
{
    try (BufferedInputStream in = bufferedInputStream;
         BufferedOutputStream out = bufferedOutputStream) 
    {
        byte[] buf = new byte[1024];
        int nosRead;
        while ((nosRead = in.read(buf)) != -1)  // read this carefully ...
        {
            out.write(buf, 0, nosRead);
        }
    }
}

如您所见,我已经摆脱了虚假的“catch and squash exception”处理程序,并使用 Java 7+ try with resources修复了资源泄漏。

还有几个问题:

  1. 复制函数最好将文件名字符串(FilePath对象)作为参数并负责打开流。

  2. 鉴于您正在进行块读取和写入,使用缓冲流几乎没有价值。(实际上,它可能会使 I/O 变慢。)最好使用普通流并使缓冲区大小与Buffered*类使用的默认缓冲区大小相同或更大。

  3. 如果您真的关心性能,请尝试transferFrom按照此处所述使用:


1 - 理论上,如果bufferedInputStream.close()抛出异常,bufferedOutputStream.close()调用将被跳过。实际上,关闭输入流不太可能引发异常。但无论哪种方式,try with resource方法都会正确地处理这个问题,而且更加简洁。


推荐阅读