简体   繁体   中英

File Splitting code in Java is not working as required

I have tried the following code for file splitting, it works but not as required. ie an mp3 file names 'song.mp3' has size of 2540KB, expected number of chunks (100KB each) are 25 but codes gives only 12 chunks, I don't understand the reason.

        public static void main(String[] args) throws IOException {

              File file = new File("song.mp3");

    FileInputStream fIn = new FileInputStream("song.mp3");
    FileOutputStream fOut = new FileOutputStream("song_0.mp3");
    int chunk_size = 1024 * 100;
    byte[] buff = new byte[chunk_size]; // 100KB file
    int i = 0;
    String file_name = file.getName();
    String file_name_base = file_name.substring(0, 
    file_name.lastIndexOf("."));
    while (fIn.read() != -1) {

        fIn.read(buff);
        int total_read = 0;
        total_read += chunk_size;
        long read_next_chunk = total_read;
        String file_name_new = file_name_base + "_" + i + ".mp3";
        File file_new = new File(file_name_base);
        i++;
        fOut = new FileOutputStream(file_name_new);
        fOut.write(buff);

        fIn.skip(total_read);// skip the total read part

    } // end of while loop

    fIn.close();
    fOut.close();

}

Your code is sure that does not work, at least because:
at each iteration you read 1 Byte and throw it away by while (fIn.read() != -1) .
Change your loop to something like this:

int bytesReadCounter;
while((bytesReadCounter = fIn.read(buff, 0, chunk_size)) > 0){
    .................................. 
    fOut.write(buff, 0, bytesReadCounter);
    ..................................
}

In buff you store the Bytes read and in bytesReadCounter the number of Bytes read.
Then you write to fOut from buff exactly bytesReadCounter Bytes.

Edit , use this code:

public static void main(String[] args) {
    File file = new File("song.mp3");

    FileInputStream fIn = null;
    try {
        fIn = new FileInputStream(file);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }

    int chunk_size = 1024 * 100;
    byte[] buff = new byte[chunk_size]; // 100KB file
    int i = 0;
    String file_name = file.getName();
    String file_name_base = file_name.substring(0, file_name.lastIndexOf("."));
    int bytesReadCounter;
    boolean hasMore = true;
    while (hasMore) {
        try {
            bytesReadCounter = fIn.read(buff, 0, chunk_size);
        } catch (IOException e) {
            e.printStackTrace();
            break;
        }

        hasMore = bytesReadCounter > 0;

        if (!hasMore)
            break;

        String file_name_new = file_name_base + "_" + i + ".mp3";
        File file_new = new File(file_name_new);

        FileOutputStream fOut = null;
        try {
            fOut = new FileOutputStream(file_new);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            break;
        }

        try {
            fOut.write(buff, 0, bytesReadCounter);
            fOut.close();
        } catch (IOException e) {
            e.printStackTrace();
            break;
        }

        i++;
    }

    try {
        fIn.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

I would like to immediately propose the newer Path/Files classes.

The following code is not tested, but has a clear buffered reading:

  • read the original file sequentially
  • loop the parts
  • write a part using a buffer ( block ) - which is faster than a mere FileInputStream

So:

public static void splitFile(String file) throws IOException {
    final long partSize = 100 * 1024;

    Path originalPath = Paths.get(file);
    if (Files.isReadable(originalPath)) {
        throw new FileNotFoundException("Is not a readable file: " + originalPath);
    }
    // Read the file:
    long totalSizeToRead = Files.size(originalPath);
    try (InputStream in = Files.newInputStream(originalPath)) {
        int partNo = 0;
        byte[] block = new byte[16 * 1024];
        // Write parts
        while (totalSizeToRead > 0) {
            // Write part
            ++partNo;
            Path partPath = Paths.get(String.format("%s-%03d.part", file, partNo));
            int sizeToReadInPart = partSize;
            if (totalSizeToRead < sizeToReadInPart) {
                sizeToReadInPart = (int) totalSizeToRead;
            }
            try (OutputStream out = Files.newOutputStream(partPath,
                                    StandardOpenOptions.REPLACE_EXISTING)) {
                // Write blocks of part
                while (sizeToReadInPart > 0) {
                    int toRead = Math.min(block.length, sizeToReadInPart);
                    int actuallyRead = in.read(block, 0, toRead);
                    sizeToReadInPart -= actuallyRead;
                    if (actuallyRead <= 0) {
                        break;
                    }
                    out.write(block, 0, actuallyRead);
                }
            }
            totalSizeToRead -= sizeToReadInPart;
        }
    }
}

The points here:

  • try-with-resource automatically closes, even on return/break/thrown exception.
  • I used the file size (a Long) and int for Buffer sizes, needs some care. Also it is a redundancy, given actuallyRead .
  • Java by convention uses camelCase instead of underscores for normal variables.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM