Search code examples
javajavafxunzipprogress

Java - Unzip and Progress Bar


My program uses Tasks from JavaFX to download and unzip files and to show the progress on the screen, by using the updateProgress(workDone, max) method and the progressProperty().bind(observable) method. It works for Download :

package com.franckyi.lan.installer;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Paths;

import javafx.concurrent.Task;

public class DownloadTask extends Task<Void> {

    private String file;
    private String url;

    public DownloadTask(String dir, String fileurl) {
        file = dir;
        url = fileurl;
    }

    @Override
    protected Void call() throws Exception {
        URLConnection connection = new URL(url).openConnection();
        long fileLength = connection.getContentLengthLong();

        try (InputStream is = connection.getInputStream();
                OutputStream os = Files.newOutputStream(Paths.get(file))) {

            long nread = 0L;
            byte[] buf = new byte[1024];
            int n;
            while ((n = is.read(buf)) > 0) {
                os.write(buf, 0, n);
                nread += n;
                updateProgress(nread, fileLength);
            }
        }
        return null;
    }

    @Override
    protected void succeeded(){
        System.out.println("Download succeeded");
    }

}

But it doesn't work well for Unzip : The file is correctly unzipped but I get a wrong ProgressBar (empty at the end).

package com.franckyi.lan.installer;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javafx.concurrent.Task;

public class UnZipTask extends Task<Void>{

    private File zipfile;
    private File folder;

    public UnZipTask(File zipfile, File folder){
        this.zipfile = zipfile;
        this.folder = folder;
    }

    @Override
    protected Void call() throws Exception {
        ZipInputStream zis = new ZipInputStream(
            new BufferedInputStream(new FileInputStream(zipfile.getCanonicalFile())));
        ZipEntry ze = null;
        try {
            while ((ze = zis.getNextEntry()) != null) {
                 File f = new File(folder.getCanonicalPath(), ze.getName());
                if (ze.isDirectory()) {
                    f.mkdirs();
                    continue;
                }
                f.getParentFile().mkdirs();
                OutputStream fos = new BufferedOutputStream(new FileOutputStream(f));
                try {
                    try {
                        final byte[] buf = new byte[1024];
                        int bytesRead;
                        long nread = 0L;
                        long length = zipfile.length();

                        while (-1 != (bytesRead = zis.read(buf))){
                            fos.write(buf, 0, bytesRead);
                            nread += bytesRead;
                            System.out.println(nread + "/" + length);
                            updateProgress(nread, length);
                        }
                    } finally {
                        fos.close();
                    }
                } catch (final IOException ioe) {
                    f.delete();
                    throw ioe;
                }
            }
        } finally {
            zis.close();
        }
        return null;
    }

    @Override
    protected void succeeded(){
        System.out.println("Unzip succeeded");
    }

}

This is what I get in the console :

Download succeeded
1024/91804
2048/91804
2815/91804
362/91804
326/91804
290/91804
386/91804
257/91804
250/91804
588/91804
1101/91804
1613/91804
2128/91804
2646/91804
3159/91804
3672/91804
4185/91804
4701/91804
5214/91804
5731/91804
6243/91804
6755/91804
7272/91804
7793/91804
8326/91804
8862/91804
9379/91804
9897/91804
10411/91804
10927/91804
11442/91804
11956/91804
12437/91804
447/91804
437/91804
978/91804
1525/91804
2040/91804
454/91804
1056/91804
1568/91804
2089/91804
2672/91804
3198/91804
3728/91804
4282/91804
4826/91804
5377/91804
5891/91804
6413/91804
6941/91804
7480/91804
8027/91804
8565/91804
9088/91804
9609/91804
9794/91804
507/91804
1019/91804
1531/91804
2043/91804
2239/91804
134/91804
548/91804
1292/91804
2316/91804
2584/91804
507/91804
837/91804
135/91804
486/91804
1001/91804
1514/91804
2027/91804
2545/91804
3057/91804
3571/91804
4086/91804
4599/91804
5113/91804
5627/91804
6144/91804
6655/91804
7166/91804
7679/91804
8196/91804
8710/91804
9229/91804
9745/91804
10259/91804
10773/91804
11288/91804
11802/91804
12321/91804
12834/91804
13348/91804
13864/91804
14378/91804
14893/91804
15407/91804
15918/91804
16431/91804
16944/91804
17458/91804
17971/91804
18484/91804
18997/91804
19508/91804
20021/91804
20535/91804
21047/91804
21560/91804
22072/91804
22584/91804
23096/91804
23609/91804
24122/91804
24638/91804
25149/91804
25664/91804
26176/91804
26689/91804
27203/91804
27715/91804
28227/91804
28739/91804
29251/91804
29764/91804
30277/91804
30789/91804
31301/91804
31813/91804
32325/91804
32838/91804
33306/91804
33819/91804
34333/91804
34846/91804
35357/91804
35869/91804
36381/91804
36894/91804
37407/91804
37922/91804
38435/91804
38948/91804
39460/91804
39972/91804
40488/91804
41002/91804
41514/91804
42028/91804
42540/91804
43052/91804
43566/91804
44079/91804
44594/91804
45105/91804
45619/91804
46132/91804
46644/91804
47156/91804
47668/91804
48180/91804
48692/91804
49204/91804
49716/91804
50228/91804
50741/91804
51252/91804
51765/91804
52277/91804
52790/91804
53305/91804
53821/91804
54335/91804
54852/91804
55365/91804
55881/91804
56396/91804
56442/91804
545/91804
1287/91804
2311/91804
2584/91804
507/91804
845/91804
4/91804
Unzip succeeded

Can someone help me ?


Solution

  • It is because you use length of compressed zipFile as the maximum, and the count of bytes raeded from each uncompressed zipEntry as the postion - the size of compressed file is in most cases different from the uncompressed one, also you can have multiple files in the zip package - so the progres will jump from 0 to some value (the size of actual zipEntry not the compressed zipFile length) for each one in this case. To have the actual position in a zip file, get the reference to FileChannel from the FileInputStream, using this method: FileInputStream#getChannel();

    then when it comes to update the progres do:

     updateProgress(channel.position(), length);
    

    This will update the progress bar according to the actual position that was readed in the zipFile (not the size of uncompressed content).

    It could be something like:

    package com.franckyi.lan.installer;
    
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipInputStream;
    
    import javafx.concurrent.Task;
    
    public class UnZipTask extends Task<Void>{
    
        private File zipfile;
        private File folder;
    
        public UnZipTask(File zipfile, File folder){
            this.zipfile = zipfile;
            this.folder = folder;
        }
    
        @Override
        protected Void call() throws Exception {
            FileInputStream is = new FileInputStream(zipfile.getCanonicalFile());
            FileChannel channel = is.getChannel();
            ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is));
            ZipEntry ze = null;
            try {
                while ((ze = zis.getNextEntry()) != null) {
                     File f = new File(folder.getCanonicalPath(), ze.getName());
                    if (ze.isDirectory()) {
                        f.mkdirs();
                        continue;
                    }
                    f.getParentFile().mkdirs();
                    OutputStream fos = new BufferedOutputStream(new FileOutputStream(f));
                    try {
                        try {
                            final byte[] buf = new byte[1024];
                            int bytesRead;
                            long nread = 0L;
                            long length = zipfile.length();
    
                            while (-1 != (bytesRead = zis.read(buf))){
                                fos.write(buf, 0, bytesRead);
                                nread += bytesRead;
                                System.out.println(nread + "/" + length);
                                updateProgress(channel.position(), length);
                            }
                        } finally {
                            fos.close();
                        }
                    } catch (final IOException ioe) {
                        f.delete();
                        throw ioe;
                    }
                }
            } finally {
                zis.close();
            }
            return null;
        }
    
        @Override
        protected void succeeded(){
            System.out.println("Unzip succeeded");
        }
    
    }