在Java 7中删除二进制文件的一部分的最佳方法是什么

时间:2015-09-25 16:24:33

标签: java nio

即我有一个10 MB的文件,我想删除1M到2M的字节,因此生成的文件是9mb,从文件中以2m字节开始的数据现在从1M开始

我正在使用Java 7,因此可以制作NIO,文件大小通常为10MB,并且经常通过网络访问,因此我正在寻找一种表现良好的优雅解决方案。

我知道BteBuffer.allocateDirect()和File.getChannel()但是我很难解决,如果有办法做我想要的,不需要从文件通道写入8MB到临时缓冲区只是为了将它写回到不同位置的文件,或者如果使用allocateDirect()

,这实际上是可以的

3 个答案:

答案 0 :(得分:3)

将结果写入临时文件,然后将旧文件替换为临时文件(用作磁盘上的缓冲区)。

代码示例:

public static void main(String[] args) {
    // -- prepare files
    File inFile = new File("D:/file.dat");
    File outFile;
    try {
        outFile = File.createTempFile("swap", "buffer");
    } catch (IOException ex) {
        throw new IOError(ex);
    }

    // -- process file
    try (
            InputStream inStream = new FileInputStream(inFile);
            OutputStream outStream = new FileOutputStream(outFile)
    ) {
        //drop some bytes      (will be removed)
        inStream.skip(4);
        //modify some bytes    (will be changed)
        for (int i = 0; i < 4; i++) {
            byte b = (byte) inStream.read();
            outStream.write(b >> 4);
        }
        //copy bytes in chunks (will be kept)
        final int CHUNK_SIZE = 1024;
        byte[] chunkBuffer = new byte[CHUNK_SIZE];
        while (true) {
            int chunkSize = inStream.read(chunkBuffer, 0, CHUNK_SIZE);
            if (chunkSize < 0) {
                break;
            }
            outStream.write(chunkBuffer, 0, chunkSize);
        }
    } catch (FileNotFoundException ex) {
        throw new RuntimeException("input file not found!", ex);
    } catch (IOException ex) {
        throw new RuntimeException("failed to trim data!", ex);
    }

    // -- release changes
    //replace inFile with outFile
    inFile.delete();
    outFile.renameTo(inFile);
}

答案 1 :(得分:0)

我不认为你可以将一个巨大的文件读入内存,然后切断它的某些部分。 @Binkan的方法更加智能,因为这个方法已经过高达15MB的文件测试

    try {
        // read the file 
        byte[] results = Files.readAllBytes(Paths.get("xfile.apk"));
        // strip off say 1MB
        byte[] trimmed = Arrays.copyOfRange(results, 0,
                (results.length / (1024 * 1024)) - 1);
        Files.write(Paths.get("xfile.apk"), trimmed,
                StandardOpenOption.TRUNCATE_EXISTING);
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

使用MappedByteBuffer可能会产生性能开销

答案 2 :(得分:0)

我认为在磁盘上使用临时文件的方法是一种很好的方法。如果您处于可以在磁盘上创建新临时文件的情况,那么nio确实有一些可以帮助您的选项。我刚刚在这里讨论API和nio教程,但似乎FileChannel.transferFromFileChannel.transferTo可能是您需要的工具。

我没有测试过以下代码,但它应该指向正确的方向。

public static void main(String[] args) {
    int megaByte = 1024 * 1024;
    // prepare the paths
    Path inPath = Paths.get("D:/file.dat"); // java.nio.file.Paths
    Path outPath; // java.nio.file.Path
    try {
        outPath = Files.createTempFile(null, "swp"); // java.nio.file.Files
    } catch (IOException ex) {
        throw new IOError(ex);
    }

    // process the file
    try (
            FileChannel readChannel = new FileChannel.open(inPath, READ);
            FileChannel writeChannel = new FileChannel.open(outPath, WRITE, TRUNCATE_EXISTING)
    ) {
        long readFileSize = readChannel.size();
        long expectedWriteSize = readFileSize;
        if (readFileSize > 2 * megabyte)
            expectedWriteSize = readFileSize - megabyte;
        else if (readFileSize > megabyte)
            expectedWriteSize = megabyte;
        // copy first megabyte (or entire file if less than a megabyte)
        long bytesTrans = readChannel.transferTo(0, megabyte, writeChannel);
        // copy everything after the second megabyte
        if (readFileSize > 2 * megabyte)
            bytesTrans += readChannel.transferTo(2 * megabyte, readFileSize - 2 * megabyte, writeChannel);
        if (bytesTrans != expectedWriteSize)
            System.out.println("WARNING: Transferred " + bytesTrans + " bytes instead of " + expectedWriteSize);
    } catch (FileNotFoundException ex) {
        throw new RuntimeException("File not found!", ex);
    } catch (IOException ex) {
        throw new RuntimeException("Caught IOException", ex);
    }

    // replace the original file with the temporary file
    try {
        // ATOMIC_MOVE may cause IOException here . . .
        Files.move(outPath, inPath, REPLACE_EXISTING, ATOMIC_MOVE);
    } catch (IOException e1) {
        try {
            // . . . so it's probably worth trying again without that option
            Files.move(outPath, inPath, REPLACE_EXISTING);
        } catch (IOException e2) {
            throw new IOError(e2);
        }
    }
}

即使您无法打开新文件,nio也有可能提供帮助。如果您打开文件的读/写通道或在同一文件上打开两个不同的通道,可能会使用transferTo方法将文件的一部分传输到文件的另一部分。我没有足够的经验来了解它。 API声明采用显式位置参数的方法(如transferTo的第一个参数)可以与写入文件的操作同时进行,因此我不会将其排除。如果您尝试,可能需要以兆字节大小的块重写该文件。如果确实有效,那么FileChannel.truncate可以用来在文件中将部分文件写入文件中的1兆字节位置时切断文件的最后一兆字节。