java:使用 nio 复制时创建的损坏的 zip 文件

java : corrupted zip file created when copy using nio

我已经实现了以下代码来复制文件(二进制文件) 代码

private void copyFileWithChannels(File aSourceFile, File aTargetFile) {
        log("Copying files with channels.");        
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        FileInputStream inStream = null;
        FileOutputStream outStream = null;      
        try {
            inStream = new FileInputStream(aSourceFile);
            inChannel = inStream.getChannel();
            outStream = new  FileOutputStream(aTargetFile);        
            outChannel = outStream.getChannel();
            long bytesTransferred = 0;
            while(bytesTransferred < inChannel.size()){
                bytesTransferred += inChannel.transferTo(0, inChannel.size(), outChannel);
            }
        }
        catch(FileNotFoundException e){
            log.error("FileNotFoundException in copyFileWithChannels()",e);
        }
        catch (IOException e) {
            log.error("IOException in copyFileWithChannels()",e);           
        }
        catch (Exception e) {
            log.error("Exception in copyFileWithChannels()",e);
        }
        finally {
            try{
                if (inChannel != null) inChannel.close();
                if (outChannel != null) outChannel.close();
                if (inStream != null) inStream.close();
                if (outStream != null) outStream.close();
            }catch(Exception e){
                log.error("Exception in copyFileWithChannels() while closing the stream",e);
            }
        }

    }

我有一个压缩文件的测试代码。当我验证文件时,我发现生成的文件已损坏(大小增加)。 源 zip 文件大约 9GB。

试试这个:

  while(bytesTransferred < inChannel.size()){
      bytesTransferred += inChannel.transferTo(bytesTransferred, inChannel.size() - bytesTransferred, outChannel);
  }

另外,我会参考IOUtils的实现,作为参考

https://github.com/apache/commons-io/blob/master/src/main/java/org/apache/commons/io/FileUtils.java

特别是

private static void doCopyFile(final File srcFile, final File destFile, final boolean preserveFileDate)

transferTo 方法的第一个参数给出传输的起始位置,不是相对于流停止的位置,而是相对于文件的开头。因为你把 0 放在那里,所以它总是从文件的开头开始传输。所以该行需要

bytesTransferred += inChannel.transferTo(bytesTransferred , inChannel.size(), outChannel);

mavarazy 在他的回答中提到,他不确定在使用 inChannel.size() 时是否需要循环,因为预期是如果您提供整个大小,它将复制整个文件。但是,如果输出通道的缓冲区可用空间较少,实际传输可能会少于请求的字节数。所以你确实需要他的第二个代码片段中的循环。

除非你有充分的理由最好使用 Files.copy(Path, Path, CopyOption...)